原文地址: http://www.srtsolutions.com/generics-the-better-method-match?utm_source=feedburner&utm_medium=feed&utm_campaign=Feed%3A+billwagner+%28Bill+Blogs+in+C%23%29

 

这个问题又出现在我参与分享的一个C#开发人员邮件列表中。因为还有这一主题的合理数量混乱,我想我会发布规则以及这些规则背后的原因。

 

这里发布的所有东西都来自<<C#语言规范>>(第四版)  7.5.2节(类型引用) 和7.5.3节(重载解析).

 

考虑这两个Log的重载方法:

    

static void Log<K,V>(K key, V value)
static void Log<K,V>(string key, IDictionary<K, V> values)

假如你调用下列代码:

 

class FooDict : Dictionary<string, int>
{
}
// create custom dictionary
FooDict fooDict = new FooDict();
// should *not* use the IDictionary<K,V>
Log("FooDict", fooDict);
// try a regular dictionary value
Dictionary<string, int> dict = fooDict;
// should *not* use the IDictionary<K,V> 
Log("Dictionary", dict);
// now try the EXACT type
IDictionary<string, int> idict = fooDict;
// should use the IDictionary<K,V> - does
Log("IDictionary", idict);
 
注释描述了代码的调用执行情况,为什么会有这样的结果?
 
作任何操作执行之前,编译器会先寻找所有合适的候选方法,那些候选方法可能包括泛型方法。如果方法调用不指定参数具体类型,编译器就会推断参数类型。Eric Lippert在说明的解释已经很详细了(如下)
类型推断规则被设计成回答一个问题:仅给定实参和形式参数类型,什么是最合适的实参被推断成每个类型参数。
对于第一个重载方法,Log<string, FooDict>是推断出最匹配的参数类型。现在分析下重载的运算规则,有两个候选方法。使用类型推断,第一个重载方法更匹配,因为调用第一个重载方法是确定转换:Log<string, FooDict> 是一个来自于Log<string, FooDict>的确定转换,调用第二个重载方法则是一个继承转换,Log<string, FooDict> 到Log<string, IDictionary<string, int>>的继承转换,因为FooDict类继承IDictionary<string, int>接口。确定转换优先于接口转换。
同样的处理逻辑可以应用于其它的继承转换:比如转换成父类,长度转换(如int类型转成long类型)或者用户定义类继承转换。
 
引用规范和说”就是这样工作运行的”是一样的,但是为什么这是正确的选择对于编程语言来说呢?简略地说,因为接口类型在真正编译的时候类型转换才起作用(注:泛型是生成后就已经起作用了)。 我完成的部分已经发布到这里 C# Puzzlers Live Lessons
 
 
以下是本人写的完整的测试运行代码
class Program
    {
        static void Main(string[] args)
        {
            // create custom dictionary
            FooDict fooDict = new FooDict();
            // should *not* use the IDictionary<K,V>
            Logger.Log("FooDict", fooDict);
            // try a regular dictionary value
            Dictionary<string, int> dict = fooDict;
            // should *not* use the IDictionary<K,V> 
            Logger.Log("Dictionary", dict);
            // now try the EXACT type
            IDictionary<string, int> idict = fooDict;
            // should use the IDictionary<K,V> - does
            Logger.Log("IDictionary", idict);
        }
    }
     class FooDict : Dictionary<string, int>
    {
    }
    class Logger
    { 
        public static void Log<K, V>(string key, IDictionary<K, V> values)
        {
            Console.WriteLine("call:Log<K, V>(string key, IDictionary<K, V> values)");
        }
        public static void Log<K, V>(K key, V value)
        {
            Console.WriteLine("call:Log<K,V>(K key, V value)");
        }
    }
posted on 2012-03-02 15:04  ybq155  阅读(277)  评论(0编辑  收藏  举报