这是C#4中动态绑定的漏洞吗?

5

我在Fabio Maulo's的博客上看到了一篇非常有趣的文章。如果您不想跳转到链接,这里有代码和错误信息。我定义了一个新的通用类,如下所示:

public class TableStorageInitializer<TTableEntity> where TTableEntity : class, new()
    {
        public void Initialize()
        {
            InitializeInstance(new TTableEntity());
        }

        public void InitializeInstance(dynamic entity)
        {
            entity.PartitionKey = Guid.NewGuid().ToString();
            entity.RowKey = Guid.NewGuid().ToString();
        }

    }

请注意,InitializeInstance接受一个参数,类型为dynamic。现在为了测试这个类,我定义了另一个嵌套在我的主程序类中的类,如下所示:
class Program
        {
            static void Main(string[] args)
            {
               TableStorageInitializer<MyClass> x = new TableStorageInitializer<MyClass>();
                x.Initialize();
            }
            private class MyClass
            {
                public string PartitionKey { get; set; }
                public string RowKey { get; set; }
                public DateTime Timestamp { get; set; }
            }
        }

注意:内部类"MyClass"被声明为私有。
现在,如果我运行这段代码,我会在"entity.PartitionKey = Guide.NewGuid().ToString()"这一行上得到一个Microsoft.CSharp.RuntimeBinder.RuntimeBinderException
然而,有趣的是异常消息说"对象不包含PartitionKey的定义"。
alt text http://img697.imageshack.us/img697/4188/testdl.png
另请注意,如果您将嵌套类的修饰符更改为public,则代码将无问题执行。那么,在幕后到底发生了什么?请参考任何文档 - 当然,如果这些内容有文档记录的话。
1个回答

5
绑定器试图创建一个可访问的类来处理对象,这种情况下,调用该代码的程序并不“知道”MyClass类,因此也不“知道”PartitionKey
我不知道C# 4规范中是否有详细说明-我知道我曾与Chris Burrows进行过电子邮件交流,所以详细信息可能在他的博客上:)请记住,动态绑定随时间而变化,因此最近的帖子可能更准确地反映了RTM代码。
认为如果您将PartitionKey放入公共接口中,私有类会实现它,那么可能会起作用-但您必须尝试一下。
动态类型存在各种“陷阱”。显式接口实现也存在类似的问题:
public int Count(IList list)
{
   int count1 = list.Count; // Fine
   dynamic d = list;
   int count2 = d.Count; // Should work, right?
}

如果您传递一个数组,这将失败 - 因为尽管 IList.Count 存在,但它在数组中是显式实现的 - 就像这样:
string[] array = new string[10];
Console.WriteLine(array.Count); // Won't compile

绑定器试图将对象视为其具体类型,而不是视为 IList,因此失败...


我曾经认为使用dynamic是使用普通反射的简写,但显然我错了,因为如果您将InitializeInstance的实现替换为使用反射,则可以正常工作。您能详细说明一下吗? - Ahmed
@Jon:我还是不太明白数组是如何逃脱实现所有接口属性的(尽管将其强制转换为IList<string>可以调用Count!?)- 你能否详细解释一下?(另外,你上面是不是指的d.Count?) - BlueRaja - Danny Pflughoeft
没事了,我自己找到答案了:http://msdn.microsoft.com/en-us/library/aa288461%28VS.71%29.aspx。 - BlueRaja - Danny Pflughoeft
@BlueRaja: 我认为这是因为,正如Jon所说,它在数组中“明确实现。”“实现接口的类可以明确实现该接口的成员。当成员被明确实现时,它不能通过类实例访问,只能通过接口实例访问。” 由于数组是数组实例而不是IList的实例,因此它没有为“Count”属性定义的实现。当然,我可能会略过一些关键点。 好文推荐:--> http://msdn.microsoft.com/en-us/library/aa288461%28VS.71%29.aspx - Pandincus
@BlueRaja:是的,我指的是d.Count。已经修复了。@Galilyou:它使用反射,但更加智能:它考虑了从调用上下文中可以访问哪些类等因素。 - Jon Skeet

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接