1. 程式人生 > >【轉】編寫高質量代碼改善C#程序的157個建議——建議42:使用泛型參數兼容泛型接口的不可變性

【轉】編寫高質量代碼改善C#程序的157個建議——建議42:使用泛型參數兼容泛型接口的不可變性

-c out bsp counter nag oid 需求 turn ring

建議42:使用泛型參數兼容泛型接口的不可變性

讓返回值類型返回比聲明的類型派生程度更大的類型,就是“協變”。如:

        public Employee GetAEmployee(string name)
        {
            Console.WriteLine("我是雇員:"+name);
            return new Programmer() { Name = name };//Programmer是Employee的子類
        }

Programmer是Employee的子類,所以Programmer對象也是Employee對象。方法GetAEmployee返回一個Programmer的對象,也就是相當於返回一個Employee對象。

由於協變是一種如此自然的應用,我們很可能寫出如下代碼:

    class Program
    {
        static void Main(string[] args)
        {
            ISalary<Programmer> s = new BaseSalaryCounter<Programmer>();
            PrintSalary(s);
        }

        static void PrintSalary(ISalary<Employee> s)
{
s.Pay();
} }
interface ISalary<T> { void Pay(); } class BaseSalaryCounter<T> : ISalary<T> { public void Pay() { Console.WriteLine("Pay base salary"); } } class Employee { public string Name { get; set; } }
class Programmer : Employee { } class Manager : Employee { }

在PrintSalary這個方法中,方法接收的類型是ISalary<Employee>。於是,我們想當然的認為ISalary<Programmer>必然也可以被PrintSalary方法接收的。事實卻不然,代碼編譯會通不過:

無法從“MyTest.ISalary<MyTest.Programmer>”轉換為“MyTest.ISalary<MyTest.Employee>”

編譯器對於接口和委托類型參數的檢查是非常嚴格的,除非用關鍵字out特別聲明,不然這段代碼只會編譯失敗。要讓PrintSalary完成需求,我們可以使用泛型類型參數:

        static void PrintSalary<T>(ISalary<T> s)
        {
            s.Pay();
        }

註意:建議開頭指出“協變”是針對返回值而言的,但是所舉的這個例子並沒有體現“返回值”這個概念。實際上,只有泛型類型參數在一個接口聲明中不被用來作為方法的輸入參數,我們就姑且把它看成是“返回值”類型的。所以,本建議中這種模式是滿足“協變”定義的。但是,只要將T作為輸入參數,就不滿足“協變”定義了。

轉自:《編寫高質量代碼改善C#程序的157個建議》陸敏技

【轉】編寫高質量代碼改善C#程序的157個建議——建議42:使用泛型參數兼容泛型接口的不可變性