Почему я не могу этого сделать: dynamic x = new ExpandoObject {Foo = 12, Bar = "двенадцать" }

Я делаю что-то неправильно, или следующий код действительно невозможен?

dynamic x = new ExpandoObject { Foo = 12, Bar = "twelve" };

Если это действительно невозможно, существует ли еще один однострочный способ создания объекта ExpandoObject с двумя свойствами?

Почему команда С# откажется от того же синтаксиса инициализации, что и для обычных объектов, анонимных объектов и перечислений/списков?

Обновление

Я задал этот вопрос, потому что я пытался показать энтузиасту Pearl классные новые динамические функции С#, но затем я был застопорен тем, что не мог делать то, что, как я думал, было логическим экземпляром ExpandoObject. Благодаря ответу Ханса Пассана, я понимаю, что ExpandoObject был неправильным инструментом для работы. Моя реальная цель состояла в том, чтобы использовать динамические функции С# для возврата двух именованных значений из метода. Как указывает Ханс, ключевое слово dynamic идеально подходит для этого. Для этого мне не нужен ExpandoObject со всеми его накладными расходами.

Итак, если вы хотите вернуть пару именованных значений из метода, и вас не интересует безопасность типа, Intellisense, рефакторинг или производительность, это работает довольно хорошо:

public dynamic CreateFooBar()
{
    return new { Foo = 42, Bar = "Hello" };
}

Использование:

dynamic fooBar = CreateFooBar();
var foo = fooBar.Foo;
var bar = fooBar.Bar;

Ответ 1

Я делаю что-то неправильно, или следующий код действительно невозможен?

Это действительно невозможно. Вещью слева от оператора присваивания должно быть свойство или поле, известные во время компиляции, и, очевидно, это не относится к объектам expando.

Почему команда С# откажется от того же синтаксиса инициализации, что и для обычных объектов, анонимных объектов и перечислений/списков?

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

Первым шагом в реализации любой функции является то, что кто-то должен думать об этом в первую очередь. Насколько мне известно, мы никогда этого не делали. В частности, человеку, разрабатывающему инициализаторы объектов в 2006 году, было бы довольно сложно узнать, что в 2010 году мы собирались добавить "динамический" язык и соответствующим образом разработать функцию. Особенности всегда разрабатываются дизайнерами, которые двигаются вперед вовремя, а не назад во времени. Мы помним только прошлое, а не будущее.

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

Я бы не ожидал, что это произойдет в ближайшее время; мы немного заняты этим целым бизнесом async-and-WinRT, который мы анонсировали в Build на прошлой неделе.

Ответ 2

Там лучше ловушка мыши, чем ExpandoObject. Динамическое ключевое слово обрабатывает анонимные типы с помощью aplomb:

class Program {      
    static void Main(string[] args) {
        dynamic x = new { Foo = 12, Bar = "twelve" };
        Display(x);
    }
    static void Display(dynamic x) {
        Console.WriteLine(x.Foo);
        Console.WriteLine(x.Bar);
    }
}

Одна неудачная проблема заключается в том, что компилятор С# генерирует анонимный тип, предоставляя членам только внутреннюю доступность. Это означает, что вы получите ошибку времени выполнения при попытке доступа к членам другой сборки. Облом.

Рассмотрим кортеж, значительно улучшенный в С# v7.

Ответ 3

Dynamitey (с открытым исходным кодом PCL и найденный в nuget) имеет синтаксис для инициализации expandos, который может быть встроенным.

 //using Dynamitey.DynamicObjects
 var x = Build<ExpandoObject>.NewObject(Foo:12, Bar:"twelve");

Ответ 4

Один способ, который работал у меня, - это метод расширения ToExpando() от Yan Cui (source):

public static class DictionaryExtensionMethods
{
    /// <summary>
    /// Extension method that turns a dictionary of string and object to an ExpandoObject
    /// </summary>
    public static ExpandoObject ToExpando(this IDictionary<string, object> dictionary)
    {
        var expando = new ExpandoObject();
        var expandoDic = (IDictionary<string, object>) expando;

        // go through the items in the dictionary and copy over the key value pairs)
        foreach (var kvp in dictionary)
        {
            // if the value can also be turned into an ExpandoObject, then do it!
            if (kvp.Value is IDictionary<string, object>)
            {
                var expandoValue = ((IDictionary<string, object>) kvp.Value).ToExpando();
                expandoDic.Add(kvp.Key, expandoValue);
            }
            else if (kvp.Value is ICollection)
            {
                // iterate through the collection and convert any strin-object dictionaries
                // along the way into expando objects
                var itemList = new List<object>();
                foreach (var item in (ICollection) kvp.Value)
                {
                    if (item is IDictionary<string, object>)
                    {
                        var expandoItem = ((IDictionary<string, object>) item).ToExpando();
                        itemList.Add(expandoItem);
                    }
                    else
                    {
                        itemList.Add(item);
                    }
                }

                expandoDic.Add(kvp.Key, itemList);
            }
            else
            {
                expandoDic.Add(kvp);
            }
        }

        return expando;
    }
}

Пример использования:

public const string XEntry = "ifXEntry";
public static readonly dynamic XEntryItems = new Dictionary<string, object>
{
    { "Name",                     XEntry + ".1" },
    { "InMulticastPkts",          XEntry + ".2" },
    { "InBroadcastPkts",          XEntry + ".3" },
    { "OutMulticastPkts",         XEntry + ".4" },
    { "OutBroadcastPkts",         XEntry + ".5" },
    { "HCInOctets",               XEntry + ".6" },
    { "HCInUcastPkts",            XEntry + ".7" },
    { "HCInMulticastPkts",        XEntry + ".8" },
    { "HCInBroadcastPkts",        XEntry + ".9" },
    { "HCOutOctets",              XEntry + ".10" },
    { "HCOutUcastPkts",           XEntry + ".11" },
    { "HCOutMulticastPkts",       XEntry + ".12" },
    { "HCOutBroadcastPkts",       XEntry + ".13" },
    { "LinkUpDownTrapEnable",     XEntry + ".14" },
    { "HighSpeed",                XEntry + ".15" },
    { "PromiscuousMode",          XEntry + ".16" },
    { "ConnectorPresent",         XEntry + ".17" },
    { "Alias",                    XEntry + ".18" },
    { "CounterDiscontinuityTime", XEntry + ".19" },
}.ToExpando();

Затем можно использовать такие свойства, как XEntryItems.Name.

PS: Пожалуйста, проголосуйте здесь, чтобы поддерживать инициализаторы объектов в ExpandoObjects.

Ответ 5

Такой синтаксис инициализатора возможен, потому что уже есть свойство с get и setter. С объектом expando еще нет этих свойств из того, что я могу сказать.