C# 如果匿名类型对象不可枚举,它如何转换为字符串?

C# 如果匿名类型对象不可枚举,它如何转换为字符串?,c#,C#,假设我定义了这样一个变量: var o = new { RBI = 108, Name = "Roberto Alamar" }; 我可以这样做: Console.WriteLine("{0}", o); 但如果我尝试: foreach (var i in o) { Console.WriteLine("{0}", o[i]); } 我得到一个错误: foreach statement cannot operate on variables of type 'AnonymousTy

假设我定义了这样一个变量:

var o = new { RBI = 108, Name = "Roberto Alamar" };
我可以这样做:

Console.WriteLine("{0}", o);
但如果我尝试:

foreach (var i in o) {
    Console.WriteLine("{0}", o[i]);
}
我得到一个错误:

foreach statement cannot operate on variables of type 'AnonymousType#1' because 'AnonymousType#1' does not contain a public definition for 'GetEnumerator'

那么它是如何在引擎盖下工作的呢?我认为将对象转换为字符串的方法必须遍历所有属性才能完成任务。是否有某种特殊的方法允许这种情况发生,或者我误解了它的工作原理?

所有匿名对象都有相同的方法。它们可以相互比较,只要它们具有相同类型的相同命名字段,它们都有一个ToString实现,将提供您可以看到的字符串。但是他们没有枚举器的实现。他们为什么要这样做?从这个意义上讲,它不像Javascript那样可以枚举属性名/索引/任何东西,因为。。。是C,不是这样的。你为什么会有不同的想法

如果您希望类似的工作方式,幸运的是我们有隐式类型的变量和反射来帮助我们

var obj = new { Foo = "asd", Bar = "add", Gar = "123" };
var adapter = PropertyAdapter.Create(obj);
foreach (var name in adapter)
    Console.WriteLine("obj.{0} = {1}", name, adapter[name]);

所有匿名对象都有相同的方法。它们可以相互比较,只要它们具有相同类型的相同命名字段,它们都有一个ToString实现,将提供您可以看到的字符串。但是他们没有枚举器的实现。他们为什么要这样做?从这个意义上讲,它不像Javascript那样可以枚举属性名/索引/任何东西,因为。。。是C,不是这样的。你为什么会有不同的想法

如果您希望类似的工作方式,幸运的是我们有隐式类型的变量和反射来帮助我们

var obj = new { Foo = "asd", Bar = "add", Gar = "123" };
var adapter = PropertyAdapter.Create(obj);
foreach (var name in adapter)
    Console.WriteLine("obj.{0} = {1}", name, adapter[name]);
当你这样做的时候

Console.WriteLine("{0}", o);
真正发生的是对Object.ToString的调用,它是继承的,并且具有用于打印属性和值的匿名类型的内置实现

另一方面,

foreach (var i in o) { .. }
无法工作,因为o必须是IEnumerable或IEnumerable

编辑:在使用WriteLine打印的字符串上进行枚举时,为了便于解释,可以实现与预期枚举相同的效果,否则执行以下操作将毫无用处:

foreach (var i in o.ToString()) { .. }
然而,正如Jeff Mercado指出的,这似乎不是您想要的,它不会在属性上迭代—只在已经格式化的字符串的单个字符上迭代

Console.WriteLine("{0}", o);
真正发生的是对Object.ToString的调用,它是继承的,并且具有用于打印属性和值的匿名类型的内置实现

另一方面,

foreach (var i in o) { .. }
无法工作,因为o必须是IEnumerable或IEnumerable

编辑:在使用WriteLine打印的字符串上进行枚举时,为了便于解释,可以实现与预期枚举相同的效果,否则执行以下操作将毫无用处:

foreach (var i in o.ToString()) { .. }

然而,正如Jeff Mercado指出的,这并不是您想要的,它不会在属性上迭代-只在已经格式化的字符串的单个字符上迭代-您不能这样做,因为匿名类型没有实现IEnumerable接口-它不是一个集合,只是一个对象。必须显式打印这些值

Console.WriteLine("{0}", o.RBI);
Console.WriteLine("{0}", o.Name);
但是你自己退一步。你需要匿名类型吗?定义自己的自定义类型

class MyType // give it more meaningful name 
{
     public int RBI { get; set;}
     public string Name { get; set;}
}

您不能这样做,因为匿名类型不实现IEnumerable接口——它不是一个集合,只是一个对象。必须显式打印这些值

Console.WriteLine("{0}", o.RBI);
Console.WriteLine("{0}", o.Name);
但是你自己退一步。你需要匿名类型吗?定义自己的自定义类型

class MyType // give it more meaningful name 
{
     public int RBI { get; set;}
     public string Name { get; set;}
}
它在引擎盖下是如何工作的?我认为将对象转换为字符串的方法必须遍历所有属性才能完成任务

您的假设是ToString的实现在所有匿名类型的所有实例之间共享;例如,有一个助手在逻辑上与JavaScript类似:

var s = "";
for (property in this)
   s += property + ":" + this[property];
这种假设是错误的;对于匿名类型,ToString没有单一的一刀切的实现。相反,编译器知道匿名方法的所有属性是什么,因此它为每个不同的匿名类型生成一个全新的ToString自定义实现

在C语言中,foreach循环与JavaScript中for-In循环不同。C循环枚举集合的成员。JS循环枚举对象的属性

如果要在C中枚举对象的属性,可以这样做,只需多做一点工作:

var s = "";
foreach (PropertyInfo propertyInfo in this.GetType().GetProperties())
   s += propertyInfo.Name + ":" + propertyInfo.GetValue(this).ToString();
它在引擎盖下是如何工作的?我认为将对象转换为字符串的方法必须遍历所有属性才能完成任务

您的假设是ToString的实现在所有匿名类型的所有实例之间共享;例如,有一个助手在逻辑上与JavaScript类似:

var s = "";
for (property in this)
   s += property + ":" + this[property];
这种假设是错误的;没有单独的一个人 ize适用于匿名类型的ToString的所有实现。相反,编译器知道匿名方法的所有属性是什么,因此它为每个不同的匿名类型生成一个全新的ToString自定义实现

在C语言中,foreach循环与JavaScript中for-In循环不同。C循环枚举集合的成员。JS循环枚举对象的属性

如果要在C中枚举对象的属性,可以这样做,只需多做一点工作:

var s = "";
foreach (PropertyInfo propertyInfo in this.GetType().GetProperties())
   s += propertyInfo.Name + ":" + propertyInfo.GetValue(this).ToString();


你认为当你说1.ToString时有循环吗?@AnthonyPegram谢谢你的评论。我认为它给了我所需要的洞察力。为什么你会认为,仅仅因为它可能有一个循环作为一个实现细节,它应该不遗余力地生成额外的代码并膨胀二进制文件,只是为了通过一个IEnumerable来展示它循环的东西,而没有人会使用它?你对面向对象的设计有一些奇怪的想法。你是一个JavaScript程序员吗?C foreach循环不是in循环的JavaScript。C foreach循环枚举集合的成员。in循环的JavaScript枚举*对象的属性。“他们很相似,但完全不同。”埃里克·利珀特,你抓住我了。我很乐意接受你的回答,如果你回答的细节有点充实的话。你认为当你说1.ToString时有循环吗?@AnthonyPegram谢谢你的评论。我认为它给了我所需要的洞察力。为什么你会认为,仅仅因为它可能有一个循环作为一个实现细节,它应该不遗余力地生成额外的代码并膨胀二进制文件,只是为了通过一个IEnumerable来展示它循环的东西,而没有人会使用它?你对面向对象的设计有一些奇怪的想法。你是一个JavaScript程序员吗?C foreach循环不是in循环的JavaScript。C foreach循环枚举集合的成员。in循环的JavaScript枚举*对象的属性。“他们很相似,但完全不同。”埃里克·利珀特,你抓住我了。我很乐意接受,如果你回答这个问题时把细节充实了一点。他并没有试图迭代字符串表示的字符,至少看起来不是这样,而是试图枚举对象的字段并访问其值。与Javascript类似。这是不正确的-编译器使用特定于类型的版本重写匿名类型的object.ToString方法。没有任何东西是继承的。他不是试图迭代字符串表示的字符,至少看起来不是这样,而是试图枚举对象的字段并访问其值。与Javascript类似。这是不正确的-编译器使用特定于类型的版本重写匿名类型的object.ToString方法。没有什么是遗传的,你抓住我了。我以为它会像我更熟悉的其他语言一样。你抓住了我。我以为这会像我更熟悉的其他语言一样。只是一个小小的警告:如果有任何索引属性,您的代码将失败。@kvb:的确;如果属性是只写的呢?这个草图有很多问题;它不是为了防弹的。只是一个小小的警告:如果有任何索引属性,您的代码将失败。@kvb:确实;如果属性是只写的呢?这个草图有很多问题;它不是为了防弹的。