Получит ли .NET выгоду от именованных анонимных типов? - PullRequest
17 голосов
/ 17 марта 2009

Учтите это:

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

Ответы [ 7 ]

13 голосов
/ 17 марта 2009

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

public object MyMethod()
    {
        var myNewObject = new
        {
            stringProperty = "Hello, World!",
            intProperty = 1337,
            boolProperty = false
        };

        return myNewObject;
    }

    public T Cast<T>(object obj, T type)
    {
        return (T)obj;
    }

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

var obj = MyMethod();
var myNewObj = Cast(obj, new { stringProperty = "", intProperty = 0, boolProperty = false });

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

9 голосов
/ 10 мая 2009

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

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

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

var me = GetMe();

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

public var GetMeFrom(var names)
{
    return new { FirstName = names["First"], LastName = names["Last"] };
}

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

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

5 голосов
/ 17 марта 2009

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

Вариант 1:

Если бы вы могли объявить класс следующим образом:

public class MyClass
{ properties={ int Number, int Square, int Absolute, IEnumerable<int> Range } }

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

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

Вариант 2:

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

Вариант 3:

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

4 голосов
/ 17 марта 2009

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

Я думаю, они были бы хорошим дополнением к 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, я думаю, что подход конструктора модулей - лучший способ сделать что-то подобное.

1 голос
/ 20 января 2011

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

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

0 голосов
/ 20 января 2011

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

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

(int, string, Person) tuple = (8, "hello", new Person());

эквивалентно:

Tuple<int,string,Person> tuple = new Tuple<int,string,Person>(8 ,"hello", new Person());

В функции:

public (int, string, Person) GetTuple(){
    return ...
}

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

int number = tuple[1];
string text = tuple[2];
Person person = tuple[3];
0 голосов
/ 17 марта 2009

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

...