Нечитаемая только альтернатива анонимным типам

В С# анонимный тип может быть следующим:

method doStuff(){
     var myVar = new {
         a = false, 
         b = true
     }

     if (myVar.a) 
     {
         // Do stuff             
     }
}

Однако следующее не будет компилироваться:

method doStuff(){
     var myVar = new {
         a = false, 
         b = true
     }

     if (myVar.a) 
     {
         myVar.b = true;
     }
}

Это потому, что поля myVar доступны только для чтения и не могут быть назначены. Кажется, что желание сделать что-то наподобие последнего довольно часто; возможно, лучшим решением, которое я видел, является просто определение структуры вне метода.

Однако нет ли другого способа сделать работу над этим блоком? Причина, по которой это меня беспокоит, myVar - это локальная переменная этого поля, поэтому кажется, что ее следует использовать только внутри метода, который ее использует. Кроме того, необходимость размещения структуры вне метода может сделать объявление объекта довольно далеким от его использования, особенно в длинном методе.

Поставить другим способом, есть ли альтернатива анонимным типам, которые позволят мне определить такую ​​структуру (я понимаю, что структура существует в С# и должна быть определена вне метода), не делая ее доступной только для чтения? Если нет, есть ли что-то принципиально неправильное в том, что вы хотите сделать это, и должен ли я использовать другой подход?

Ответ 1

Нет, вам нужно создать свой собственный класс или структуру, чтобы сделать это (предпочтительнее класс, если вы хотите, чтобы он был изменчивым - изменчивые структуры ужасны).

Если вам не нужны реализации Equals/ToString/GetHashCode, это довольно просто:

public class MyClass {
    public bool Foo { get; set; }
    public bool Bar { get; set; }
}

(Я по-прежнему использую свойства, а не поля, по разным причинам.)

Лично я обычно обнаруживаю, что хочу неизменный тип, который я могу передать между методами и т.д. - мне нужна именованная версия существующей функции анонимного типа...

Ответ 2

Есть ли альтернатива анонимным типам, которые позволят мне точно определить простой тип записи, например, без его чтения?

Нет. Вы должны будете сделать номинальный тип.

Если нет, есть ли что-то принципиально неправильное в желании сделать это?

Нет, это разумная функция, которую мы рассмотрели ранее.

Я отмечаю, что в Visual Basic анонимные типы являются изменяемыми, если вы хотите, чтобы они были.

Единственное, что действительно "принципиально неверно" в отношении изменяемого анонимного типа, заключается в том, что было бы опасно использовать его как хэш-ключ. Мы разработали анонимные типы с предположениями, что (1) вы собираетесь использовать их в качестве ключей в equijoins в понимании запросов LINQ и (2) в LINQ-to-Objects и других реализациях, объединения будут реализованы с использованием хеш-таблиц. Поэтому анонимные типы должны быть полезны в качестве хеш-ключей, а изменчивые хеш-ключи опасны.

В Visual Basic реализация GetHashCode не использует никакой информации из изменяемых полей анонимных типов. Хотя это разумный компромисс, мы просто решили, что в С# дополнительная сложность не стоит усилий.

Ответ 3

Для вышеуказанных типов операций вы должны определить свой собственный изменяемый STRUCT. Смещаемые структуры могут представлять собой головную боль для авторов компилятора, таких как Eric Lippert, и есть некоторые неудачные ограничения в том, как .net обрабатывает их, но тем не менее семантику изменчивых структур "Обычные старые данные" (структуры, в которых все поля являются общедоступными, и единственные публичные функции, которые пишут this, являются конструкторами или называются исключительно из конструкторов) предлагают гораздо более ясную семантику, чем может быть достигнуто с помощью классов.

Например, рассмотрим следующее:

struct Foo {
  public int bar; 
  ...other stuff;
}
int test(Action<Foo[]> proc1, Action<Foo> proc2)
{
  foo myFoos[] = new Foo[100];
  proc1(myFoos);
  myFoos[4].bar = 9;
  proc2(myFoos[4]); // Pass-by-value
  return myFoos[4].bar;
}

Предполагая, что там нет небезопасного кода и что делегаты, прошедшие через входящие вызовы, могут быть вызваны и вернутся за конечное время, что вернет test()? Тот факт, что Foo является структурой с открытым полем bar, достаточен для ответа на вопрос: он вернет 9, независимо от того, что еще появляется в объявлении Foo, и независимо от того, какие функции передаются в proc1 и proc2. Если Foo был классом, нужно было бы изучить все Action<Foo[]> и Action<Foo>, которые существуют или будут когда-либо существовать, чтобы знать, что вернет test(). Определение того, что Foo является структурой с открытым полем bar, кажется намного проще, чем рассмотрение всех прошлых и будущих функций, которые могут быть переданы.

Методы Struct, которые изменяют this, особенно плохо обрабатываются в .net, поэтому, если вам нужно использовать метод для изменения структуры, почти наверняка лучше использовать один из этих шаблонов:

  myStruct = myStruct.ModifiedInSomeFashion(...);  // Approach #1
  myStructType.ModifyInSomeFashion(ref myStruct, ...);  // Approach #2

чем шаблон:

  myStruct.ModifyInSomeFashion(...);

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

Ответ 4

Вы не сможете получить хороший синтаксис инициализации, но класс ExpandoObject, представленный в .NET 4, станет жизнеспособным решением.

dynamic eo = new ExpandoObject();

eo.SomeIntValue = 5;
eo.SomeIntValue = 10; // works fine

Ответ 5

В С# 7 мы можем использовать named tuples, чтобы сделать трюк:

(bool a, bool b) myVar = (false, true);

if (myVar.a)
{
    myVar.b = true;
}