[C#] Пара вопросов по структуре и "правильному" программингу
только лучше, если это будут короткие куски кода, которые показывают только суть.
На одном из форумов, где обсуждался этот вопрос нашел такой пример:
C++
class wheel{
friend class car;//car can access wheel without any restriction
friend class bus;//bus can access wheel without any restriction
//other declarations for wheel
};
class car{
//declarations for car
};
class bus{
//declarations for bus
};
т.е. у нас и автобусы и машины имеют монопольный доступ к колесам. Был предложен следующий метод решения:
public abstract class Vehicle
{
protected class Wheel
{
// details of wheel
}
protected Wheel wheel;
// vehicle details
}
public class Car : Vehicle
{
// car specifics
}
public class Bus : Vehicle
{
// bus specifics
}
Но ведь это не эквивалентное решение. Тем самым я вообще закрываю доступ к колесам для внешнего мира. Ладно, допустим, мы можем сденать класс колес открытым. Но предположим, что через пару лет мне этот класс потребовался для описания... ну скажем колеса обозрения. И тоже с полным доступом. Было бы странно наследовать колесо обозрения от Vehicle.
Еще одна проблема, которую можно было бы решить при помощи const.
Разрабатывается класс для управления некоторой структурой данных. В идеале, хотелось бы, что бы при любом изменение структуры выполнялся бы некоторый код (ну, скажем выставление флага, что структура изменена). Однако читать из структуры нужно позволить без ограничений. Можно было бы реализовать две функции, одна из которых возвращала бы константную ссылку на структуру и вторая — ссылку с полным доступом. Это могло бы гарантировать, что ни я сам, ни кто-либо после меня не смогли бы написать код, модифицирующий структуру без выставления флага.
Было бы странно наследовать колесо обозрения от Vehicle.INDEED
Пара вопросов по структуре и "правильному" программингу
Засунь в жопу «правильный программинг», напиши 10 удачных проектов с некрасивой структурой.
Правильность в лице UML-пидораса скорее плохо, чем хорошо.
через пару лет мне этот класс потребовался для описания... ну скажем колеса обозрения
я советую прекратить прием объектов некоторого класса, который ты тоже унаследуешь от класса «колесо».
И главное помни, если уже сразу слезть со всех колёс не получается, что ты не автомобиль!
Вдруг, с противной стороны, показалось другое пятнышко, увеличилось, стало растягиваться, и уже было не пятнышко. Близорукий, хотя бы надел на нос вместо очков колеса с комиссаровой брички, и тогда бы не распознал, что это такое.
На сях я частенько использовал ссылочный тип просто для создания коротких временных алиасов, просто для повышения читабельности кода. Например:
class Chasti
{
void Otorvat;
};
class Noga : public Chasti {};
class Krylo : public Chasti {};
class Muxa
{
Noga Nogi[6];
Krylo Krylja[2];
};
class Nasekomye_V_Komnate
{
Muxa MerzkajaMalenkayaTvar[1000];
};
Nasekomye_V_Komnate Vse;
...........
for (int it=0; it<1000; it++)
{
Muxa &M = Vse.MerzkajaMalenkayaTvar[it]; // Создание алиаса (!)
M.Nogi[0].Otorvat;
M.Nogi[1].Otorvat;
M.Nogi[2].Otorvat;
M.Nogi[3].Otorvat;
M.Nogi[4].Otorvat;
M.Nogi[5].Otorvat;
M.Krylja[0].Otorvat;
M.Krylja[1].Otorvat;
}
На сях я частенько использовал ссылочный тип просто для создания коротких временных алиасов, просто для повышения читабельности кода.так и пиши
var тварь = Все.МерзкаяТварь[i];
а еще лучше сразу
foreach (var тварь in Все.МерзкаяТварь)
Например по поводу friend:используй вместо этого internal.
Разрабатывается класс для управления некоторой структурой данных. В идеале, хотелось бы, что бы при любом изменение структуры выполнялся бы некоторый код (ну, скажем выставление флага, что структура изменена).можно тоже самое сделать через два interface-а: readonly и не-readonly.
Первую проблему предлагают решать используя internal методы, тогда доступ к ним имеется у классов в рамках сборки. Для реализации константных-неконстатных возвратов можно просто свойства организовать, но это не так эффективно.
foreach во-первых можно использовать не во всех случаях, а во-вторых... ты уверен, что переменная value-type объявленная в foreach будет ссылкой?
ты уверен, что переменная value-type объявленная в foreach будет ссылкой?слабо попробовать?
http://msdn.microsoft.com/en-us/library/bb383973.aspx
Если можно статически вычислить тип переменной без его явного объявления, тогда можно использовать var.
Например,
var label = new Label;
выглядит немного приятнее, чем
Label label = new Label;
Со свойствами тоже не понятно... если свойство будет возвращать структуру целиком, то я могу с тем же успехом туда начать писать. Значит, что бы этого не допустить — надо писать свойства на каждый элемент структуры. Если в структуру будут добавляться поля — потребуется писать для них новые свойства. Соответственно новые свойства я должен буду писать как на чтение, так и на запись. А теперь вспомним зачем мне это надо было? Что бы при записи вызывался определенный код. А если свойства на запись будут разнесены по пространству-времени, то контроллировать этот процес будет гораздо труднее. Т.к. я должен буду ПОСТОЯННО ПОМНИТЬ(!) про то, что при добавлении нового свойства — необходимо вызвать такую-то функцию
насчет internal — не понимаю, как это может помочь? Если мне нужно сделать другом теперь и здесь только один вот этот конкретный класс (колесо обозрения в моем случае)internal - это типа "мы тут все в одной помойке"
дружить с кем-то из другой помойки - низя
Со свойствами тоже не понятно... если свойство будет возвращать структуру целиком, то я могу с тем же успехом туда начать писатьа чо ты делаешь в С++ если свойство возвращает массив?
как ты не даешь тудыть писать?
сейчас считаю обсуждение совершенно бессмысленым - ты говоришь об одном, тебе говорят о другом, ты снова о другом...
выглядит немного приятнее, чем
ну, во-первых это тока "Beginning in Visual C# 3.0", а во-вторых нововведение выглядит довольно подозрительно — ИМХО это только уменьшает читабельность кода
слабо попробовать?
Попробовал — выскочила ошибка компиляции CS1654 "Cannot modify members of 'uu_el' because it is a 'foreach iteration variable'"
а чо ты делаешь в С++ если свойство возвращает массив?
как ты не даешь тудыть писать?
Как обычно — возвращаю константный массив, например:
int Arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 0};
const int* RetArr
{
return Arr;
}
int _tmain(int argc, _TCHAR* argv[])
{
const int* A1 = RetArr;
int* A2 = RetArr; // error C2440: 'initializing' : cannot convert from 'const int *' to 'int *'
A1[3] = 7; // error C3892: 'A2' : you cannot assign to a variable that is const
return 0;
}
сходи в любой книжный и купи книгу по сишарп за 300р
Книг полно, читал
затем напиши что-нибудь на этом языке
Уже написал (примерно год назад) На шарпе писал UI, аналитику писал на C++, взаимодействие шло через COM
потом приходи, если вопросы, конечно, останутццо
Как видишь — вопросы остались
Попробовал — выскочила ошибка компиляции CS1654 "Cannot modify members of 'uu_el' because it is a 'foreach iteration variable'"зачем ты пытался изменить переменную, которую использовал в foreach?
ты типа сам себе запрещаешь писать в тот массив?
защита от самого себя? имхо это бессмысленно
в консте есть смысл чтобы запретить другим писать а не мне самому
const int* getMy {
int* c = new int[1];
}
int main {
int* a = getMy; // error: invalid conversion from `const int*' to `int*'
a[0] = 5; // error: assignment of read-only location
return 0;
}
но!
оказывается и в данном случае это бессмысленно, потому как легко обходится
#include <iostream>
const int* getMy {
int* c = new int[1];
}
int main {
int* a = (int *)getMy;
a[0] = 5;
std::cout << a[0];
return 0;
}
т.е. const не нужен вообще
вот его и убрали
зачем ты пытался изменить переменную, которую использовал в foreach?ну он же написал, затем что это возможно в С++ и типа удобно
можно тоже самое сделать через два interface-а: readonly и не-readonly.Можно поподробнее, это как?
вот такой способ не проходит:
public struct TTT
{
public Int16 yyy1;
public Int16 yyy2;
public Int16 yyy3;
}
public readonly TTT GetReader // ERROR: The modifier 'readonly' is not valid for this item
{
return uu;
}
зачем ты пытался изменить переменную, которую использовал в foreach?Потому, что именно это мне и требуется в реальной задаче. А зачем бы мне еще так нужны были бы именно ссылки?
вот такой способ не проходит:и где тут два интерфейса?
помойму имелось ввиду:
interface ForEveryone {
Field1 getField1;
Field2 getField2;
}
interface OnlyForMe : ForEveryone {
setFields1(Field1);
setFields2(Field2);
}
какой смысл в твоем примере?Ну, когда работаешь в группе смысл очень даже есть.
Приведение в смысле C вообще зло, но всегда остается const_cast<>
Но const является больше механизмом для упорядочения кода. Зная, что есть некая функция getMy написанная дядей Васей из соседнего отдела ты скорее всего напишешь int* a = getMy а не int* a = (int *)getMy. И только, когда выскочит ошибка компиляции будет повод задуматься, "а зачем это дядя Вася возвращает именно константный массив". Основное правило таково, что приведения типов надо избегать. А при необходимости включения в программу искусственного приведения типов заставляет сфокусировать внимание на этом коде
interface OnlyForMe
А что помешает другим программистам использовать этот интерфейс?
Ну и опять-таки это не решает проблему выполнения кода при записи. Как реализовать интерфейс OnlyForMe так, что при выполнении любого его метода выполнялся бы некоторый код. Все, что я могу придумать — вставить вызов этого кода в каждый метод интерфейса, а в заголовке интерфейса написать большими буквами коментарий, что бы так поступали все разработчики после меня. Но даже в этом случае остается вероятность, что человек, который придет на мое место через 3 года забудет прочитать этот коментарий. Единственный надежный способ заставить программиста обратить внимание на некоторые особенности реализации — это ошибка компиляции.
interface ForEveryone {это мерзкое влияние Java-ы
Field1 getField1;
Field2 getField2;
}
interface OnlyForMe : ForEveryone {
setFields1(Field1);
setFields2(Field2);
}
так должно быть
interface IMyReadonly
{
Field Field1{get;}
Field Field2 {get;}
}
interface IMy:IMyReadonly
{
Field Field1 {get;set;}
Field Field2 {get;set;}
}
А что помешает другим программистам использовать этот интерфейс?все тоже самое, что и в твоем примере с const, к этому интерфейсу надо будет явно приводить.
interface IA
{
int A {get;}
}
interface IAFull:IA
{
int A {get;set;}
}
class System
{
public IA GetA;
}
IAFull a = new System.GetA; ///ошибка компиляции
в любом случае, как я уже описывал, это не решает проблему
это мерзкое влияние Java-ыНу... конкретный синтаксис — дело десятое
Ну и опять-таки это не решает проблему выполнения кода при записи. Как реализовать интерфейс OnlyForMe так, что при выполнении любого его метода выполнялся бы некоторый код. Все, что я могу придумать — вставить вызов этого кода в каждый метод интерфейса, а в заголовке интерфейса написать большими буквами коментарий, что бы так поступали все разработчики после меня. Но даже в этом случае остается вероятность, что человек, который придет на мое место через 3 года забудет прочитать этот коментарий. Единственный надежный способ заставить программиста обратить внимание на некоторые особенности реализации — это ошибка компиляции.не понял
а как ты решишь это проблему на другом языке?
а как ты решишь это проблему на другом языке?Хотя, да... туплю...
Можно же этот код вставить в GetA.
Лан... резюмирую:
1. френды и консты в шарпе отсутствуют но соответствующим изменением алгоритма и мировоззрения вопчем-то обходяццо.
2. Со ссылками по-прежнему не понятно
------------------------------------------------------
Всем спасибо,
пошел пить кофе
пошел пить кофене пей - козлёночком станешь!
2. Со ссылками по-прежнему не понятноа чо тут непонятного?
это скорей религия, тут можно долго спорить зло они или добро
в том же с++ это всего лишь сахар чтобы не писать & при вызове и * при получении
в других языках ваще есть аут и инаут параметры
менять значение при форич по валутайпу - тут получается с++ круче
но вощем-то это не так часто используется, можно поскрипеть зубами и сделать обычный фор с индексом
Или Select.
Или Select.тоже не даст
Тут был намёк на функциональный подход: на старый массив забиваем, при желании пишем в него новый.
В том числе и потому, что более производительно.
Потому что собственно select (ака мап) сам по себе редко когда нужен. Обычно же он встречается в сочетании с where (aka filter например (о более сложных штуках и не говорю).
И тут внезапно обнаруживается, что делать inplace filter на обычном массиве дико тормозно, а на каком-нибудь дереве или скиплисте — тоже дико тормозно из-за полного отсутствия кэш-френдлинесс.
Дополнительно следует учесть, что в отличие от говноплюсов дотнетовский менеджер памяти дико эффективен.
Так что нефиг страдать фигнёй, если нужно быстро запрогать и чтобы быстро работало — пишем в функциональном стиле, если хочется анально оптимизировать дальше — ну да, используем структуры, простые массивы (потому что List<T> не умеет отдавать ссылку на элемент, если T - структура, by design) и аккуратно разворачиваем обработку так, чтобы она работала с ними как с ref параметрами. А чего вы хотели от анальной оптимизации?
Оставить комментарий
4223080
Начал потихоньку осваивать сей штук и натолкнулся на несколько крайне непривычных для меня особенностей, отличающих C# от C++1. Отсутствие friend.
2. Отсутствие const-параметров
3. Я туплю, или в шарпе и впрямь нельзя создать ссылку на value-type (я не про передачу аргументов)
4. ... (чо-та вроде было еще, но щас забылось...)
Как вы обходите эти ограничения? Может надо сломать какие-то стереотипы?