国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發設計 > 正文

編寫高質量代碼改善C#程序的157個建議[協變和逆變]

2019-11-17 03:09:06
字體:
來源:轉載
供稿:網友

編寫高質量代碼改善C#程序的157個建議[協變和逆變]

前言

本文已更新至http://m.survivalescaperooms.com/aehyok/p/3624579.html。本文主要學習記錄以下內容:

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

  建議43、讓接口中的泛型參數支持協變

  建議44、理解委托中的協變

  建議45、為泛型類型參數指定協變

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

讓返回值類型返回比聲明的類型派生程度更大的類型,就是“協變”。協變不是一種新出現的技術,在以往的編碼中,我們已經在不自覺地使用協變。以下的代碼就是一個不自覺應用協變的例子:

    class Employee    {        public string Name { get; set; }    }    class PRogrammer : Employee    {             }    class Program    {        public Employee GetAEmployee(string name)        {            Console.WriteLine("我是雇員:"+name);            return new Programmer() { Name = name };        }        static void Main(string[] args)        {        }    }

Programmer是Employee的子類,所以一個Programmer對象也就是一個Employee對象。方法GetAEmployee返回一個Programmer的對象,也就是相當于返回了一個Employee對象。正是因為在FCL4.0以前的版本中,協變是如此自然的一種應用,所以我們很有可能寫出這樣的代碼:

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

接下來看一下Main函數中的調用:

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

咋眼一看,問題應該不大,不過編譯器卻出現了錯誤。

編譯器對于接口和委托類型參數的檢查是非常嚴格的,除非用關鍵字out特別聲明(這個會在下一個建議中進行闡述),不然這段代碼只會編譯失敗,要讓PrintSalary完成需求,我們可以使用泛型類型參數:

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

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

建議43、讓接口中的泛型參數支持協變

除了建議42中提到的使用泛型參數兼容泛型接口的不可變性外,還有一種辦法就是為接口中的泛型聲明加上out關鍵字來支持協變,如下所示:

namespace Consoleapplication10{    class Employee    {        public string Name { get; set; }    }    class Programmer : Employee    {             }    interface ISalary< out T>    {        void Pay();    }    class BaseSalaryCounter<T> : ISalary<T>    {        public void Pay()        {            Console.WriteLine("Pay base salary");        }    }    class Program    {        static void Main(string[] args)        {            ISalary<Programmer> s = new BaseSalaryCounter<Programmer>();            PrintSalary(s);        }        static void PrintSalary(ISalary<Employee> s)        {            s.Pay();        }    }}

out關鍵字是FCL4.0中新增的功能,它可以在泛型接口和委托中使用,用來讓類型參數支持協變性。通過協變,可以使用比聲明的參數派生類型更大的參數。通過以上的例子我們應該能理解這種應用。

FCL4.0對多個接口進行了修改以支持協變,如IEnumerable<out T>、IEnumerator<out T>、IQueryable<out T>等。由于IEnumerable<out T>現在支持協變,所以上段代碼在FCL4.0中能運行得很好。

在我們自己的代碼中,如果要編寫泛型接口,除非確定該接口中的泛型參數不涉及變體,否則都建議加上out關鍵字。協變增大了接口的使用范圍,而且幾乎不會帶來什么副作用。

建議44、理解委托中的協變

委托中的泛型變量天然是部分支持協變的。為什么說是“部分支持協變”呢?來看一下下面的例子:

    class Employee    {        public string Name { get; set; }    }    class Manager:Employee    {    }    class Program    {        public delegate T GetEmployeeHandler<T>(string name);        static void Main(string[] args)        {            GetEmployeeHandler<Employee> getAEmployee = GetAManager;            Employee e = getAEmployee("Mike");            Console.ReadLine();        }        static Manager GetAManager(string name)        {            Console.WriteLine("我是經理:" + name);            return new Manager() { Name = name };        }        static Employee GetAEmployee(string name)        {            Console.WriteLine("我是雇員:"+name);            return new Employee() {Name=name };        }    }

上文中,方法GetAManager返回的是一個Manager,但是在使用中,其實是將其賦值給了一個泛型參數為Employee的委托變量。我們也許會認為委托中的泛型變量不再需要out關鍵字,這是錯誤的理解。因為存在下面這樣一種情況,所以編譯報錯:

要讓上面的代碼編譯通過,同樣需要為委托中的泛型參數指定out關鍵字:

public delegate T GetEmployeeHandler< out T>(string name);

除非考慮到該委托聲明肯定不會用于可變性,否則,為委托中的泛型參數指定out關鍵字將會拓展該委托的應用,建議在實際的編碼工作中永遠這樣使用,實際上,FCL4.0中的一些委托聲明已經用out關鍵字來讓委托支持協變了,如我們常常會使用到的:

public delegate TResult Func<out TResult>();

建議45、為泛型類型參數指定協變

逆變是指方法的參數可以是委托或泛型接口的參數類型的基類。FCL4.0中支持逆變的常用委托有:

Func<in T,out TResult>Predicate<in T>

常用泛型接口有:

ICompare<in T>

下面的例子演示了泛型類型參數指定逆變所帶來的好處:

    public interface IMyComparable<in T>    {        int Compare(T other);    }    public class Employee:IMyComparable<Employee>    {        public string Name { get; set; }        public int Compare(Employee other)        {            return Name.CompareTo(other.Name);        }    }    public class Programmer:Employee,IMyComparable<Programmer>    {        public int Compare(Programmer other)        {            return Name.CompareTo(other.Name);        }    }    public class Manager : Employee    {         }    class Program    {        static void Main(string[] args)        {            Programmer p = new Programmer() { Name = "Mike" };            Manager m = new Manager() { Name = "aehyok" };            Test(p, m);        }        static void Test<T>(IMyComparable<T> t1, T t2)        {             //省略        }    }

在上面的這個例子中,如果不為接口IMyComparable的泛型參數T指定in關鍵字,將會導致Test(p,m)編譯錯誤。由于引入了接口的逆變性,這讓方法Test支持了更多的應用場景。在FCL4.0之后版本的實際編碼中應該始終注意這一點。

英語小貼士

1、Then, please give me a new reservation.——那么,請幫我重新訂位。

2、Sorry, this flight is full.——抱歉,這班飛機已客滿。

3、What is the possibility of my gettinga seat if I wait?——若是我在此等候,有機位的機率有多大?

4、When will the next flight to Los Angeles leave?——下一班飛往洛杉機的班機何時起飛?

5、The day after tomorrow, Friday.——后天,星期五。

6、That will be fine. What's the flightnumber and departure time?——太好了。請告訴我班機號碼與起飛時間?

7、What is the fare?——費用多少?

作者:aehyok

出處:http://m.survivalescaperooms.com/aehyok/

感謝您的閱讀,如果您對我的博客所講述的內容有興趣,那不妨點個推薦吧,謝謝支持:-O。


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 新民市| 贞丰县| 涟水县| 蛟河市| 三亚市| 邵阳县| 闸北区| 新宾| 乐平市| 元氏县| 南江县| 阿尔山市| 永泰县| 广元市| 曲周县| 万山特区| 南郑县| 凉城县| 南安市| 新安县| 信宜市| 福贡县| 清新县| 靖西县| 商丘市| 若尔盖县| 嵩明县| 霍山县| 新余市| 太仓市| 双江| 兴仁县| 奈曼旗| 邵阳市| 巴彦淖尔市| 股票| 京山县| 台江县| 遂宁市| 陆河县| 景泰县|