1

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

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'

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

4

4 回答 4

9

它是如何在引擎盖下工作的?我认为将对象转换为字符串的方法必须遍历所有属性才能完成任务。

您的假设是 ToString 的实现在所有匿名类型的所有实例之间共享;例如,有一些帮助器在逻辑上类似于你在 JavaScript 中所做的事情:

var s = "";
for (property in this)
   s += property + ":" + this[property];

这个假设是错误的;对于匿名类型,没有一个通用的 ToString 实现。相反,编译器知道匿名方法的所有属性是什么,因此它会为每个不同的匿名类型生成一个全新的 ToString 自定义实现。

在 C# 中,循环不像 JavaScript 中foreach的循环那样做。for-inC# 循环枚举集合的成员。JS 循环枚举对象的属性

如果你想在 C# 中枚举一个对象的属性,你可以这样做,它只需要更多的工作:

var s = "";
foreach (PropertyInfo propertyInfo in this.GetType().GetProperties())
   s += propertyInfo.Name + ":" + propertyInfo.GetValue(this).ToString();
于 2012-04-25T15:07:46.120 回答
3

你不能这样做,因为匿名类型没有实现 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;}
}
于 2012-04-25T02:12:43.130 回答
2

所有匿名对象都有相同的方法。只要它们具有相同类型的相同命名字段,它们就可以相互比较,它们都有一个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]);
public static class PropertyAdapter
{
    public static PropertyAdapter<T> Create<T>(T obj)
    {
        return new PropertyAdapter<T>(obj);
    }
}

public class PropertyAdapter<T> : IEnumerable<string>
{
    private T obj;
    public PropertyAdapter(T obj) { this.obj = obj; }

    public override string ToString()
    {
        return obj.ToString();
    }

    public object this[string name]
    {
        get
        {
            return typeof(T).GetProperty(name).GetValue(obj, null);
        }
    }

    public IEnumerator<string> GetEnumerator()
    {
        return typeof(T)
            .GetProperties()
            .Select(pi => pi.Name)
            .GetEnumerator();
    }

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
}
于 2012-04-25T02:11:34.817 回答
-1

当你这样做

Console.WriteLine("{0}", o);

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

另一方面,

foreach (var i in o) { .. }

不能工作,因为o必须是 IEnumerable(或 IEnumerable<>)

编辑:通过执行以下操作可以实现(出于解释目的,否则无用),与您期望的对使用时打印的字符串的枚举等效:WriteLine

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

然而,正如 Jeff Mercado 指出的那样,这不是您想要的(它不会遍历属性 - 只会遍历已经格式化的字符串的单个字符)

于 2012-04-25T02:12:23.380 回答