我做错了什么,或者以下代码真的不可能?
dynamic x = new ExpandoObject { Foo = 12, Bar = "twelve" };
如果真的不可能,是否有另一种单行方式来实例化具有两个属性的ExpandoObject?
为什么C#团队会选择禁止使用与常规对象,匿名对象和枚举/列表相同的初始化语法?
更新
我问了这个问题,因为我试图向珍珠爱好者展示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;
答案 0 :(得分:47)
我做错了什么,或者以下代码真的不可能?
这真的不可能。赋值运算符左侧的东西必须是编译时已知的属性或字段,显然对于expando对象不是这样。
为什么C#团队会选择禁止使用与常规对象,匿名对象和枚举/列表相同的初始化语法?
您表达问题的方式表示逻辑错误。默认情况下没有实现功能,然后我们几乎不允许使用它们,因为我们认为它们是个坏主意!默认情况下,功能未实现,必须实现才能工作。
实现任何功能的第一步是有人必须首先考虑它。据我所知,我们从未这样做过。特别是,2006年设计对象初始化器的人很难知道在2010年我们将为语言添加“动态”,并相应地设计该功能。功能始终由设计师设计,他们及时向前移动 ,而不是向后。我们只记得过去,而不是未来。
无论如何,这是一个好主意,所以感谢分享它。现在有人已经想到了它,我们就可以开始接下来的步骤了,比如决定它是否是最好的想法,我们可以花费有限的预算,设计它,编写规范,实现它,测试它,记录它和将它运送给客户。
我不希望任何这种情况很快发生;我们有点忙于上周在Build上宣布的整个异步和WinRT业务。
答案 1 :(得分:38)
有一个比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#编译器生成匿名类型,仅为成员提供内部可访问性。这意味着当您尝试访问另一个程序集中的成员时,您将收到运行时错误。长号。
考虑a tuple,在C#v7中有很大改进。
答案 2 :(得分:10)
Dynamitey(开源PCL,在nuget中找到)的initializing expandos语法可以是内联的。
//using Dynamitey.DynamicObjects
var x = Build<ExpandoObject>.NewObject(Foo:12, Bar:"twelve");
答案 3 :(得分: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";
答案 4 :(得分:2)
一个对我有用的解决方法是Yan Cui(source)的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:请投票here以支持ExpandoObjects上的对象初始值设定项。
答案 5 :(得分:0)
这种初始化器语法是可行的,因为已经存在具有get和setter的属性。对于expando对象,我所知道的还没有这些属性。
答案 6 :(得分:0)
更简单的方法:
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函数并将字典作为参数传入。 这当然可以实现为方法而不是函数。