71

我做错了什么,还是下面的代码真的不可能?

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

如果这真的不可能,是否有另一种单行方式来实例化具有两个属性的 ExpandoObject?

为什么 C# 团队会选择禁止与常规对象、匿名对象和枚举/列表相同的初始化语法?

更新

我问这个问题是因为我试图向 Pearl 爱好者展示 C# 很酷的新动态特性,但后来我因为无法做我认为是逻辑实例化的ExpandoObject. 感谢 Hans Passant 的回答,我意识到这ExpandoObject是错误的工作工具。我真正的目标是使用 C# 的动态特性从一个方法返回两个命名值。正如汉斯指出的那样,dynamic关键字非常适合这个。我不需要ExpandoObject, 及其所有开销来执行此操作。

因此,如果您想从一个方法返回一对命名值,并且您不关心类型安全、智能感知、重构或性能,这非常有效:

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

用法:

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

7 回答 7

55

我做错了什么,还是下面的代码真的不可能?

这真的不可能。赋值运算符左边的东西必须是编译时已知的属性或字段,显然这不是expando对象的情况。

为什么 C# 团队会选择禁止与常规对象、匿名对象和枚举/列表相同的初始化语法?

您提出问题的方式表明存在逻辑错误。默认情况下未实现功能,然后我们几乎禁止所有功能,因为我们认为它们是一个坏主意!功能默认未实现,必须实现才能工作。

实现任何功能的第一步是必须有人首先想到它。据我所知,我们从来没有这样做过。特别是,对于 2006 年设计对象初始化器的人来说,很难知道在 2010 年我们将在语言中添加“动态”,并相应地设计功能。功能总是由时间向前而不是时间倒退的设计师设计的。我们只记得过去,不记得未来。

无论如何,这是一个好主意,所以感谢分享。既然有人已经想到了,我们就可以开始下一步了,比如确定它是否是我们可以花费有限预算的最佳想法,设计它,编写规范,实现它,测试它,记录它并运送给客户。

我不指望这会很快发生。我们有点忙于上周在 Build 上宣布的整个异步和 WinRT 业务。

于 2011-09-19T22:34:05.690 回答
43

有比 ExpandoObject 更好的捕鼠器。dynamic关键字沉着地处理匿名类型:

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 中得到很大改进的元组。

于 2011-09-20T00:50:32.773 回答
10

Dynamitey(开源 PCL,可在 nuget 中找到)具有用于初始化可以内联的 expandos 的语法。

 //using Dynamitey.DynamicObjects
 var x = Build<ExpandoObject>.NewObject(Foo:12, Bar:"twelve");
于 2011-09-19T23:46:55.793 回答
3

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 上的对象初始值设定项。

于 2017-03-03T19:25:50.690 回答
3

当然可以!!!使用下面的扩展方法,您可以进行如下操作:

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";
于 2020-01-09T00:39:40.817 回答
0

这种初始化器语法是可能的,因为已经有带有 get 和 setter 的属性。使用 expando 对象,据我所知还没有这些属性。

于 2011-09-19T22:34:54.160 回答
-1

更简单的方法来做到这一点:

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 函数并传入字典作为参数。这当然可以实现为方法而不是函数。

于 2018-04-27T09:30:15.190 回答