Получили бы .ЧИСТУЮ выгоду от “именованных анонимных” типов?

StackOverflow https://stackoverflow.com/questions/652819

Вопрос

Подумайте об этом:

var me = new { FirstName = "John", LastName = "Smith" };

Это прекрасно, поскольку затем мы можем сделать это:

Console.WriteLine("{0} {1}", me.FirstName, me.LastName);

Однако мы не можем этого сделать:

public T GetMe()
{
    return new { FirstName = "John", LastName = "Smith" };
}

потому что мы не знаем тип T.

Мы могли бы сделать это:

public object GetMe()
{
    return new { FirstName = "John", LastName = "Smith" };
}

но тогда нам пришлось бы проверить свойства объекта с помощью отражения, чтобы получить к ним доступ:

var p = new Prog();
object o = p.GetMe();
Type t = o.GetType();
foreach (var prop in t.GetProperties())
{
    Console.WriteLine(prop.Name + ": " + prop.GetValue(o, null));
}

Однако как насчет того, если бы мы могли назвать анонимный тип так, как мы его определяем?Конечно, это больше не было бы анонимным, однако оно было бы более кратким и поддерживаемым, чем обычное определение класса.

Подумайте об этом:

public Person GetMe()
{
    return new public class Person { FirstName = "John", LastName = "Smith" };
}

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

Рассмотрим этот относительно сложный запрос Linq:

List<int> list = new List<int>();
var query = from number in list
            select
                new
                    {
                        Number = number,
                        Square = number*number,
                        Absolute = Math.Abs(number),
                        Range = Enumerable.Range(0, number)
                    };

Вместо того, чтобы определять класс следующим образом:

public class MyNumbers
{
    public int Number { get; set; }
    public int Square { get; set; }
    public int Absolute { get; set; }
    public IEnumerable<int> Range { get; set; }
}

чтобы вернуть переменную запроса из метода, мы могли бы вместо этого просто сделать это:

List<int> list = new List<int>();
return from number in list
            select new public class MyNumbers
                    {
                        Number = number,
                        Square = number*number,
                        Absolute = Math.Abs(number),
                        Range = Enumerable.Range(0, number)
                    };
Это было полезно?

Решение

На самом деле, есть «хитрость», с помощью которой можно получить обратно анонимный тип из метода.Учтите это:

родовое слово

Теперь вы можете сделать это:

родовое слово

myNewObj теперь будет объектом того же типа, что и анонимный тип.

Другие советы

Необходимая языковая функция:

родовое слово

То есть, var будет действительным как тип, возвращаемый методом, и компилятор будет выводить фактический тип из того, что возвращается. Затем вам нужно будет сделать это на сайте вызова:

родовое слово

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

родовое слово

Компилятор фактически определил бы это как универсальный метод с двумя параметрами типа: T1 - это тип имен, а T2 - тип, возвращаемый индексатором на T1, который принимает строку. T1 будет ограничен, так что у него должен быть индексатор, который принимает строку. А на сайте вызова вы просто передадите все, что имеет индексатор, принимающий строку и возвращающий любой тип, который вам нравится , и это определит тип FirstName и LastName в типе, возвращаемом GetMeFrom.

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

ИМХО, основная проблема не связана с анонимными типами, но объявление класса слишком многословно.

Вариант 1:

Если бы вы могли объявить такой класс:

родовое слово

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

Когда «компилятор как услуга» появится в C # 5, мы надеемся, что они хорошо справятся с его интеграцией, и мы сможем использовать метапрограммирование для точного решения подобных проблем. Вечеринка, как в 1958 году!

Вариант 2:

В качестве альтернативы в C # 4 вы можете просто передать анонимный тип как dynamic и избежать всех приведений типов. Конечно, это приведет к ошибкам времени выполнения, если вы переименуете переменную и т. Д.

Вариант 3:

Если бы в C # были реализованы универсальные шаблоны так же, как в C ++, вы могли бы передать анонимный тип в метод, и, если бы он имел правильные члены, он бы просто компилировался. Вы получите все преимущества безопасности статического типа без каких-либо недостатков. Каждый раз, когда мне приходится вводить код where T : ISomething на C #, меня раздражает, что они этого не сделали!

То, что вы описываете (именованные анонимные типы), в основном является "типами кортежей".

Я думаю, они были бы хорошим дополнением к C #.

Если бы я разрабатывал такую функцию для C #, я бы предоставил ее, используя такой синтаксис:

tuple<int x, int y>

чтобы вы могли сделать:

public tuple<int x, int y> GetStuff()
{
}

Затем я бы изменил определение анонимных типов, чтобы:

new { x = 2, y = 2}

имел tuple<int x, int y> поскольку это тип, а не анонимный тип.

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

Единственным недостатком такого подхода является то, что он не учитывает "ленивую" модель CLR для генерации типов.Это означает, что сборки, использующие множество различных типов кортежей, могут испытывать несколько более медленную загрузку.Лучшим подходом было бы добавить поддержку типов кортежей непосредственно в CLR.

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

Мне бы очень понравилась эта функция, я много раз хотел ее.

Хорошим примером является обработка XML.Вы анализируете их, чтобы вернуть объект, но затем вам нужно создать конкретную версию объекта, чтобы отправить обратно вызывающему.Много раз вы получаете XML, который изменяется довольно значительно и требует создания множества классов для его обработки.Разве не было бы замечательно, если бы вы могли просто создать объект, используя LinqToXml в качестве переменной, а затем просто вернуть его?

Я думаю, это было бы неплохим волшебством компилятора для кортежей:

Создание кортежа:

родовое слово

эквивалент:

родовое слово

В функции:

родовое слово

Получение значений:

родовое слово

Не могли бы вы создать интерфейс со свойствами FirstName и LastName и использовать их?

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top