[C#] Пара вопросов по структуре и "правильному" программингу

4223080

Начал потихоньку осваивать сей штук и натолкнулся на несколько крайне непривычных для меня особенностей, отличающих C# от C++
1. Отсутствие friend.
2. Отсутствие const-параметров
3. Я туплю, или в шарпе и впрямь нельзя создать ссылку на value-type (я не про передачу аргументов)
4. ... (чо-та вроде было еще, но щас забылось...)
Как вы обходите эти ограничения? Может надо сломать какие-то стереотипы? :confused:

Helga87

будет проще, если ты запостишь куски кода на С++, а тебе подскажут, как ту же самую задачу сделать на C#
только лучше, если это будут короткие куски кода, которые показывают только суть.

4223080

Например по поводу friend:
На одном из форумов, где обсуждался этот вопрос нашел такой пример:

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.
Разрабатывается класс для управления некоторой структурой данных. В идеале, хотелось бы, что бы при любом изменение структуры выполнялся бы некоторый код (ну, скажем выставление флага, что структура изменена). Однако читать из структуры нужно позволить без ограничений. Можно было бы реализовать две функции, одна из которых возвращала бы константную ссылку на структуру и вторая — ссылку с полным доступом. Это могло бы гарантировать, что ни я сам, ни кто-либо после меня не смогли бы написать код, модифицирующий структуру без выставления флага.

slonishka

Было бы странно наследовать колесо обозрения от Vehicle.
INDEED :lol:

Werdna

Пара вопросов по структуре и "правильному" программингу

Засунь в жопу «правильный программинг», напиши 10 удачных проектов с некрасивой структурой.
Правильность в лице UML-пидораса скорее плохо, чем хорошо.

slonishka

тут предлагает класс, описывающий упаковку, сделать френдом класса, описывающего калеса. :lol:

Werdna

сразу не обратил внимания...
через пару лет мне этот класс потребовался для описания... ну скажем колеса обозрения

я советую прекратить прием объектов некоторого класса, который ты тоже унаследуешь от класса «колесо».
И главное помни, если уже сразу слезть со всех колёс не получается, что ты не автомобиль!

apl13

А еще у класса "колеса" есть область видимости:
Вдруг, с противной стороны, показалось другое пятнышко, увеличилось, стало растягиваться, и уже было не пятнышко. Близорукий, хотя бы надел на нос вместо очков колеса с комиссаровой брички, и тогда бы не распознал, что это такое.

4223080

Да, вдогонку....
На сях я частенько использовал ссылочный тип просто для создания коротких временных алиасов, просто для повышения читабельности кода. Например:


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;
}

Dasar

На сях я частенько использовал ссылочный тип просто для создания коротких временных алиасов, просто для повышения читабельности кода.
так и пиши

var тварь = Все.МерзкаяТварь[i];

а еще лучше сразу

foreach (var тварь in Все.МерзкаяТварь)

Dasar

Например по поводу friend:
используй вместо этого internal.
Разрабатывается класс для управления некоторой структурой данных. В идеале, хотелось бы, что бы при любом изменение структуры выполнялся бы некоторый код (ну, скажем выставление флага, что структура изменена).
можно тоже самое сделать через два interface-а: readonly и не-readonly.

okis

Первую проблему предлагают решать используя internal методы, тогда доступ к ним имеется у классов в рамках сборки. Для реализации константных-неконстатных возвратов можно просто свойства организовать, но это не так эффективно.

4223080

что такое "var"? В MSDN чо-та ничо про это не написано... :confused:
foreach во-первых можно использовать не во всех случаях, а во-вторых... ты уверен, что переменная value-type объявленная в foreach будет ссылкой?

pitrik2

ты уверен, что переменная value-type объявленная в foreach будет ссылкой?
слабо попробовать?

EmaMoscow

> что такое "var"? В MSDN чо-та ничо про это не написано...
http://msdn.microsoft.com/en-us/library/bb383973.aspx
Если можно статически вычислить тип переменной без его явного объявления, тогда можно использовать var.
Например,
var label = new Label;

выглядит немного приятнее, чем
Label label = new Label;

4223080

насчет internal — не понимаю, как это может помочь? Если мне нужно сделать другом теперь и здесь только один вот этот конкретный класс (колесо обозрения в моем случае)
Со свойствами тоже не понятно... если свойство будет возвращать структуру целиком, то я могу с тем же успехом туда начать писать. Значит, что бы этого не допустить — надо писать свойства на каждый элемент структуры. Если в структуру будут добавляться поля — потребуется писать для них новые свойства. Соответственно новые свойства я должен буду писать как на чтение, так и на запись. А теперь вспомним зачем мне это надо было? Что бы при записи вызывался определенный код. А если свойства на запись будут разнесены по пространству-времени, то контроллировать этот процес будет гораздо труднее. Т.к. я должен буду ПОСТОЯННО ПОМНИТЬ(!) про то, что при добавлении нового свойства — необходимо вызвать такую-то функцию

pitrik2

насчет internal — не понимаю, как это может помочь? Если мне нужно сделать другом теперь и здесь только один вот этот конкретный класс (колесо обозрения в моем случае)
internal - это типа "мы тут все в одной помойке"
дружить с кем-то из другой помойки - низя

pitrik2

Со свойствами тоже не понятно... если свойство будет возвращать структуру целиком, то я могу с тем же успехом туда начать писать
а чо ты делаешь в С++ если свойство возвращает массив?
как ты не даешь тудыть писать?

Alexander08

пиздетс, чувак, читаю этот тред и немного охуеваю! сходи в любой книжный и купи книгу по сишарп за 300р. после этого прочитай ее внимательно, затем напиши что-нибудь на этом языке, например тетрис. потом приходи, если вопросы, конечно, останутццо!
сейчас считаю обсуждение совершенно бессмысленым - ты говоришь об одном, тебе говорят о другом, ты снова о другом...

4223080

выглядит немного приятнее, чем

ну, во-первых это тока "Beginning in Visual C# 3.0", а во-вторых нововведение выглядит довольно подозрительно — ИМХО это только уменьшает читабельность кода

4223080

слабо попробовать?

Попробовал — выскочила ошибка компиляции CS1654 "Cannot modify members of 'uu_el' because it is a 'foreach iteration variable'"

4223080

а чо ты делаешь в С++ если свойство возвращает массив?
как ты не даешь тудыть писать?

Как обычно — возвращаю константный массив, например:
 
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;
}

4223080

сходи в любой книжный и купи книгу по сишарп за 300р

Книг полно, читал
затем напиши что-нибудь на этом языке

Уже написал (примерно год назад) На шарпе писал UI, аналитику писал на C++, взаимодействие шло через COM
потом приходи, если вопросы, конечно, останутццо

Как видишь — вопросы остались :)

Dasar

Попробовал — выскочила ошибка компиляции CS1654 "Cannot modify members of 'uu_el' because it is a 'foreach iteration variable'"
зачем ты пытался изменить переменную, которую использовал в foreach?

pitrik2

какой смысл в твоем примере?
ты типа сам себе запрещаешь писать в тот массив?
защита от самого себя? имхо это бессмысленно
в консте есть смысл чтобы запретить другим писать а не мне самому

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 не нужен вообще
вот его и убрали

pitrik2

зачем ты пытался изменить переменную, которую использовал в foreach?
ну он же написал, затем что это возможно в С++ и типа удобно

4223080

можно тоже самое сделать через два 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;
}

4223080

зачем ты пытался изменить переменную, которую использовал в foreach?
Потому, что именно это мне и требуется в реальной задаче. А зачем бы мне еще так нужны были бы именно ссылки?

pitrik2

вот такой способ не проходит:
и где тут два интерфейса?
помойму имелось ввиду:

interface ForEveryone {
Field1 getField1;
Field2 getField2;
}

interface OnlyForMe : ForEveryone {
setFields1(Field1);
setFields2(Field2);
}

4223080

какой смысл в твоем примере?
Ну, когда работаешь в группе смысл очень даже есть.
Приведение в смысле C вообще зло, но всегда остается const_cast<>
Но const является больше механизмом для упорядочения кода. Зная, что есть некая функция getMy написанная дядей Васей из соседнего отдела ты скорее всего напишешь int* a = getMy а не int* a = (int *)getMy. И только, когда выскочит ошибка компиляции будет повод задуматься, "а зачем это дядя Вася возвращает именно константный массив". Основное правило таково, что приведения типов надо избегать. А при необходимости включения в программу искусственного приведения типов заставляет сфокусировать внимание на этом коде

4223080

interface OnlyForMe

А что помешает другим программистам использовать этот интерфейс?
Ну и опять-таки это не решает проблему выполнения кода при записи. Как реализовать интерфейс OnlyForMe так, что при выполнении любого его метода выполнялся бы некоторый код. Все, что я могу придумать — вставить вызов этого кода в каждый метод интерфейса, а в заголовке интерфейса написать большими буквами коментарий, что бы так поступали все разработчики после меня. Но даже в этом случае остается вероятность, что человек, который придет на мое место через 3 года забудет прочитать этот коментарий. Единственный надежный способ заставить программиста обратить внимание на некоторые особенности реализации — это ошибка компиляции.

Dasar

interface ForEveryone {
   Field1 getField1;
   Field2 getField2;
}
interface OnlyForMe : ForEveryone {
   setFields1(Field1);
   setFields2(Field2);
}
это мерзкое влияние Java-ы :)
так должно быть

interface IMyReadonly
{
Field Field1{get;}
Field Field2 {get;}
}
interface IMy:IMyReadonly
{
Field Field1 {get;set;}
Field Field2 {get;set;}
}

Dasar

А что помешает другим программистам использовать этот интерфейс?
все тоже самое, что и в твоем примере с const, к этому интерфейсу надо будет явно приводить.

interface IA
{
int A {get;}
}
interface IAFull:IA
{
int A {get;set;}
}

class System
{
public IA GetA;
}

IAFull a = new System.GetA; ///ошибка компиляции

4223080

в любом случае, как я уже описывал, это не решает проблему

4223080

это мерзкое влияние Java-ы
Ну... конкретный синтаксис — дело десятое :)

pitrik2

Ну и опять-таки это не решает проблему выполнения кода при записи. Как реализовать интерфейс OnlyForMe так, что при выполнении любого его метода выполнялся бы некоторый код. Все, что я могу придумать — вставить вызов этого кода в каждый метод интерфейса, а в заголовке интерфейса написать большими буквами коментарий, что бы так поступали все разработчики после меня. Но даже в этом случае остается вероятность, что человек, который придет на мое место через 3 года забудет прочитать этот коментарий. Единственный надежный способ заставить программиста обратить внимание на некоторые особенности реализации — это ошибка компиляции.
не понял
а как ты решишь это проблему на другом языке?

4223080

а как ты решишь это проблему на другом языке?
Хотя, да... туплю...
Можно же этот код вставить в GetA.
Лан... резюмирую:
1. френды и консты в шарпе отсутствуют но соответствующим изменением алгоритма и мировоззрения вопчем-то обходяццо.
2. Со ссылками по-прежнему не понятно
------------------------------------------------------
Всем спасибо,
пошел пить кофе

sergeikozyr

пошел пить кофе
не пей - козлёночком станешь!

pitrik2

2. Со ссылками по-прежнему не понятно
а чо тут непонятного?
это скорей религия, тут можно долго спорить зло они или добро
в том же с++ это всего лишь сахар чтобы не писать & при вызове и * при получении
в других языках ваще есть аут и инаут параметры
менять значение при форич по валутайпу - тут получается с++ круче
но вощем-то это не так часто используется, можно поскрипеть зубами и сделать обычный фор с индексом

agaaaa

Или Select.

nik93

Или Select.
тоже не даст

agaaaa

Тут был намёк на функциональный подход: на старый массив забиваем, при желании пишем в него новый.

bleyman

И это, кстати, правильно.
В том числе и потому, что более производительно.
Потому что собственно select (ака мап) сам по себе редко когда нужен. Обычно же он встречается в сочетании с where (aka filter например (о более сложных штуках и не говорю).
И тут внезапно обнаруживается, что делать inplace filter на обычном массиве дико тормозно, а на каком-нибудь дереве или скиплисте — тоже дико тормозно из-за полного отсутствия кэш-френдлинесс.
Дополнительно следует учесть, что в отличие от говноплюсов дотнетовский менеджер памяти дико эффективен.
Так что нефиг страдать фигнёй, если нужно быстро запрогать и чтобы быстро работало — пишем в функциональном стиле, если хочется анально оптимизировать дальше — ну да, используем структуры, простые массивы (потому что List<T> не умеет отдавать ссылку на элемент, если T - структура, by design) и аккуратно разворачиваем обработку так, чтобы она работала с ними как с ref параметрами. А чего вы хотели от анальной оптимизации?
Оставить комментарий
Имя или ник:
Комментарий: