为什么我不能这样做:dynamic x = new ExpandoObject {Foo = 12,Bar ="12"}}

dev*_*xer 60 c# dynamic expandoobject

我做错了什么,或者以下代码真的不可能?

dynamic x = new ExpandoObject { Foo = 12, Bar = "twelve" };
Run Code Online (Sandbox Code Playgroud)

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

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

更新

我问过这个问题是因为我试图向珍珠爱好者展示C#的酷炫新动态特性,但后来由于无法做到我认为是一个逻辑实例化的东西而陷入困境ExpandoObject.感谢Hans Passant的回答,我意识到这ExpandoObject是错误的工具.我的真正目标是使用C#的动态功能从方法返回两个命名值.汉斯指出,dynamic关键字对此非常完美.我没有需要一个ExpandoObject完成它的所有开销.

因此,如果您想从方法中返回一对命名值,并且您不关心类型安全性,智能感知,重构或性能,这可以很好地工作:

public dynamic CreateFooBar()
{
    return new { Foo = 42, Bar = "Hello" };
}
Run Code Online (Sandbox Code Playgroud)

用法:

dynamic fooBar = CreateFooBar();
var foo = fooBar.Foo;
var bar = fooBar.Bar;
Run Code Online (Sandbox Code Playgroud)

Eri*_*ert 48

我做错了什么,或者以下代码真的不可能?

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

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

您表达问题的方式表示逻辑错误.默认情况下没有实现功能,然后我们几乎不允许使用它们,因为我们认为它们是个坏主意!默认情况下,功能未实现,必须实现才能工作.

实现任何功能的第一步是有人必须首先考虑它.据我所知,我们从未这样做过.特别是,2006年设计对象初始化器的人很难知道在2010年我们将为语言添加"动态",并相应地设计该功能.功能始终由设计师设计,他们能够及时向前移动,而不是向后移动.我们只记得过去,而不是未来.

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

我不希望任何这种情况很快发生; 我们有点忙于上周在Build上宣布的整个异步和WinRT业务.

  • n00bs,记不起未来.:-) jk,+ 1 (8认同)
  • 对不起,埃里克,我不是故意要进攻.我只是试图向一个陌生爱好者的同事展示C#的酷炫新动态特性,但后来由于无法做出我认为是"ExpandoObject"的逻辑实例而陷入困境.无论如何,感谢您的回答,我希望您在使用async和WinRT解决问题时能够考虑此功能. (6认同)
  • 我将把它标记为答案,因为它在技术上更适合我问的确切问题,但我想提一下 Hans 的答案更好地解决了我的基本问题,即,你如何使用 C# 的动态特性返回一个方法一次有两个命名值?正如 Hans 指出的那样,您可以只使用 `dyanmic`--`ExpandoObject` 似乎适用于更复杂的场景(例如在运行时根据 XML 文件的内容构造一个类)。所以,现在我明白了这一点,我不再对`ExpandoObject` 不支持对象初始值设定项感到惊讶。 (2认同)

Han*_*ant 39

有一个比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);
    }
}
Run Code Online (Sandbox Code Playgroud)

一个不幸的问题是C#编译器生成匿名类型,只为成员提供内部可访问性.这意味着当您尝试访问另一个程序集中的成员时,您将收到运行时错误.游民.

考虑一个元组,在C#v7中有很大的改进.

  • 只要知道你必须小心地将匿名类型传递出方法的一面,它们不能跨越装配边界. (7认同)
  • 请注意,您无法在设置后更改匿名类型成员值,但可以使用ExpandoObject.这是2之间的巨大差异. (4认同)

jbt*_*ule 10

Dynamitey(开源PCL,在nuget中找到)有一个初始化可以内联的expandos的语法.

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

  • 这是我的赢家.当我想做的事情是"查看(新{项目=模特})时,我肯定很生气我必须跳过这些箍 (2认同)

小智 6

当然可以!!!使用下面的扩展方法,您可以制作如下内容:

dynamic x = (new { Foo = 12, Bar = "twelve" }).ToExpando();
Run Code Online (Sandbox Code Playgroud)

这是代码

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

Set 方法旨在添加更多属性(适用于编译时不知道属性名称的情况)

x.Set("Name", "Bill");
Run Code Online (Sandbox Code Playgroud)

因为它是动态的,所以可以使

x.LastName = "Gates";
Run Code Online (Sandbox Code Playgroud)