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


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

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

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

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

обновление

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

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

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

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

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

6 ответов:

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

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

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

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

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

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

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

есть лучшая ловушка для мыши, чем ExpandoObject. Элемент динамический ключевое слово обрабатывает анонимные типы с апломбом:

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

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

рассмотрим кортежа!--11-->, значительно улучшен в C# v7.

Dynamitey (open source PCL и найденный в nuget) имеет синтаксис для инициализация расширения что может быть inline.

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

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

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.

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

гораздо более простой способ сделать это:

Func<Dictionary<string, object>, ExpandoObject> parse = dict =>
{
    var expando = new ExpandoObject();
    foreach (KeyValuePair<string, object> entry in dict)
    {
        expando.Add(entry.Key, entry.Value);
    }
    return expando;
};

затем просто вызовите функцию parse и передайте в словарь в качестве параметра. Это, конечно, может быть реализовано в методе вместо функции.