Не доступная только для чтения альтернатива анонимным типам - PullRequest
30 голосов
/ 28 января 2012

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

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 является локальной переменной этого поля, поэтому кажется, что на него следует ссылаться только внутри метода, который его использует.Кроме того, необходимость размещения структуры вне метода может сделать объявление объекта весьма далеким от его использования, особенно в длинном методе.

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

Ответы [ 5 ]

31 голосов
/ 28 января 2012

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

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

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

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

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

24 голосов
/ 30 января 2012

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

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

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

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

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

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

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

3 голосов
/ 12 ноября 2017

В C # 7 мы можем использовать именованные кортежи , чтобы добиться цели:

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

if (myVar.a)
{
    myVar.b = true;
}
3 голосов
/ 07 июня 2016

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

dynamic eo = new ExpandoObject();

eo.SomeIntValue = 5;
eo.SomeIntValue = 10; // works fine
2 голосов
/ 30 января 2012

Для вышеперечисленных типов операций вы должны определить свою собственную изменяемую STRUCT . Изменяемые структуры могут создавать головную боль для авторов компиляторов, таких как Эрик Липперт, и есть некоторые досадные ограничения в том, как .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, гораздо проще, чем изучить все прошлые и будущие функции, которые могут быть переданы.

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

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

чем шаблон:

  myStruct.ModifyInSomeFashion(...);

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

...