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

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

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

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

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

Обновить

Я задал этот вопрос, потому что я пытался показать энтузиасту Pearl новые крутые динамические возможности 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;

8 ответов

Решение

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

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

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

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

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

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

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

Есть лучшая ловушка мыши, чем ExpandoObject. Ключевое слово dynamic обрабатывает анонимные типы с помощью 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);
    }
}

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

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

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

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

Конечно вы можете!!! с помощью расширенного метода ниже вы можете сделать что-то вроде этого:

dynamic x = (new { Foo = 12, Bar = "twelve" }).ToExpando();

вот код

public static class MyExpando
{

    public static void Set(this ExpandoObject obj, string propertyName, object value)
    {
        IDictionary<string, object> dic = obj;
        dic[propertyName] = value;
    }

    public static ExpandoObject ToExpando(this object initialObj)
    {
        ExpandoObject obj = new ExpandoObject();
        IDictionary<string, object> dic = obj;
        Type tipo = initialObj.GetType();
        foreach(var prop in tipo.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))
        {
            dic.Add(prop.Name, prop.GetValue(initialObj));
        }
        return obj;
    }
}

метод Set предназначен для добавления дополнительных свойств (для сценариев, когда вы не знаете имя свойства во время компиляции)

x.Set("Name", "Bill");

и поскольку это динамично, можно просто сделать

x.LastName = "Gates";

Один из обходных путей, который работал для меня, это ToExpando() метод расширения Ян Цуй ( источник):

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

      public static class ExpandoObjectExtentions
{
    public static void Add(this ExpandoObject obj, string key, object val)
    {
        ((IDictionary<string, object>)obj).Add(key, val);
    }
}

Намного проще сделать это:

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

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

Другие вопросы по тегам