日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > asp.net >内容正文

asp.net

.NET可变性解析(协变和逆变)

發布時間:2025/5/22 asp.net 39 豆豆
生活随笔 收集整理的這篇文章主要介紹了 .NET可变性解析(协变和逆变) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

【一】何為可變性

可變性是.NET4.0中的一個新特性,可變性可分為 : 協變性、逆變性、不可變性.

那么在.NET4.0之前是否有可變性? 答案是肯定的,我們可以通過下面的幾個實例來簡單的了解一下.NET4.0之前的協變和逆變.

實例 1 : 方法參數的協變

static void Main(string[] args) {GetProject(new Course()); // Course 繼承自 Project 此處進行了協變 } static void GetProject(Project course) {Console.WriteLine(course.Name); }

實例 2 :?數組協變以及執行時類型檢查

1 Course[] course = new Course[4]; 2 Project[] project = course; 3 project[0] = new Excercise();

在上述代碼中會拋出異常?"system.ArrayTypeMismatchException",因為從course轉換為project會返回原始引用,所以course和project都是引用的同一個數組,對于數組而言,它是一個course數組,所以會拒絕存儲對于非course類型的引用。數組的協變會導致類型安全性在執行時才能體現,而不能在編譯時體現?

可變性種類分類定義:

?協變 : 說明泛型類型參數可以從一個派生類更改為它的基類,在C#中,是用out關鍵字標記協變量形式的泛型類型參數,協變量泛型類型參數只能出現在輸出位置,比如作為方法的返回類型

?逆變 : 說明泛型類型參數可以從一個基類更改為它的派生類,在C#中,是用in關鍵字標記逆變形式的泛型類型參數,逆變量泛型類型參數只能出現在輸入位置,比如作為方法的參數。

?不可變 :按引用類型傳遞變量,可以看成是ref參數,表示傳入的類型必須與參數本身的類型完全一致,傳入方法內部的值,將同樣以相同的類型輸出。

可變性是以一種類型安全的方式,將一個對象作為另一個對象來使用,在我們面向對象編程中,繼承這一特性就很好的體現了對象的可變性. 任何使用了協變和逆變的轉換都是引用轉換,這意味著轉換之后將返回相同的引用,它不會創建新的對象,只是認為現有引用與目標類型匹配,這與某個層次中,引用類型之間的轉換是相同的。 在.NET4.0之前,泛型是不能夠進行協變和逆變的,也就是說泛型的協變和逆變是C#4.0的一個新特性,泛型的協變和逆變也是為了保持類型的絕對安全性. 在泛型接口或者委托的聲明中,.NET4.0能夠使用out修飾符來指定類型參數的協變性,使用in修飾符來指定逆變性,聲明完成之后,就可以對相關的類型進行隱式轉換了,在接口和委托中,它們的工作方式是完全相同的.

【二】泛型接口可變性

我們使用的兩個接口 : IEnumberable<T> (T 是協變的),原型為:IEnumberable<out T> ?和 IComparer<T>(T 是逆變的),原型為 : IComparer<in T>,再次記憶提示 : 如果類型參數只用于輸出,就使用out,如果只用于輸入,就用in. 更多的泛型協變接口 :?IEnumerable<T>IEnumerator<T>IQueryable<T>?和?IGrouping<TKey, TElement> 泛型逆變接口 :?IComparer<T>IComparable<T>?和?IEqualityComparer<T> 下面我們通過實例來演示一下接口的泛型可變性 實例 3 : 查看泛型接口集合IEnumberable<out T> 進行 協變 1 class Project 2 { 3 public static void GetCourseByProjects(IEnumerable<Project> projects) 4 { 5 foreach (var p in projects) 6 { 7 Console.WriteLine(p); 8 } 9 } 10 public string Name { get; set; } 11 } 12 class Course : Project 13 { 14 public static void GetCourse() 15 { 16 List<Course> courseList = new List<Course>(); 17 Project.GetCourseByProjects(courseList); 18 IEnumerable<Project> pList = courseList; 19 } 20 }

在上述代碼中,我們定義了兩個類,分別為 : projectcourse,其中course繼承自project,在project中有一個方法 GetCourseByProject,這個方法有一個形參類型為 IEnumberable<Project>,(注意 : project是course的基類,IEnumberable是可以進行協變的,那么此處的實參我們可以傳遞任何繼承自Project的類),在course中有一個方法GetCourse,這個方法用于通過course獲取到這個course 所有的projectproject.GetCourseByProject(courseList);// 此處發生了協變,原本我們的GetCourseByProject的參數類型為IEnumberable<project>,在這里我們傳遞的是它的派生類List<Course>.同理在IEnbumerbale<project> pList = courseList 也發生了協變.

實例 4 : 定義泛型接口查看逆變

1 static void Main(string[] args) 2 { 3 IBase<Course> getCourse = new Derived<Project>(); 4 } 5 public class Derived<T> : IBase<T> 6 { 7 8 public string Name { get; set; } 9 10 public void GetProject(T t) 11 { 12 Console.WriteLine("獲取到項目"); 13 } 14 } 15 public interface IBase<in T> 16 { 17 void GetProject(T t); 18 } 19 class Course : Project 20 { 21 public static void GetCourse() 22 { 23 24 } 25 } 在上述代碼中,我們定義一個泛型接口 IBase<in T>, 參數類型為" in T " 說明它是可以逆變的,同時呢,Derived<T>這個泛型類繼承自 IBase<T>,那么我們在實現的時候就可以這樣來做。 IBase<Course> courseList = new Derived<Project>(); 在我們調用的這行代碼中,將Project轉換為了他的下級類Course,所以發生了逆變。

【三】泛型委托可變性

在我們看了,泛型接口的協變和逆變之后,對于泛型委托的可變性其實性質是一樣的.我們可以通過下面兩個實例來演示一下 :

實例 5 : 委托協變

1 public delegate Project GetProject(); 2 3 static Course GetCourse() 4 { 5 return new Course(); 6 } 7 GetProject projects = GetCourse;

在上述的代碼中,我們首先定義了一個委托類型,getproject, 在GetProject projects = GetCourse,GetCourse是一個返回值為Course對象的一個函數, 此處發生了協變,Course類型轉換為了Project類型,子類轉換為父類.

實例 6 : 泛型委托協變

1 public delegate T Find<out T>(); 2 static void Main(string[] args) 3 { 4 Find<Course> getCourse = () => new Course(); // lambda 5 Find<Project> getProject = getCourse; // 發生了協變 6 }

在上述的代碼中,我們定義了一個泛型委托,Find<out T>,這里指定out說明它可以進行協變,然后在Main函數中, 首先我們通過Lambda表達式聲明了一個返回值為Course的方法,然后在將getCourse賦值給getProject,這里發生了協變.

實例 7 : 委托中的逆變

1 public delegate void FindCourse(Course course); 2 static void GetProject(Project pro) 3 { 4 Console.WriteLine(pro.Name); 5 } 6 FindCourse getCourse = GetProject;

在上述的代碼中,首先我們聲明了一個帶參數的委托FindCourse,參數類型為 Course , 然后注意在第六行代碼中, 我們將 GetProject這個方法賦值給了 委托FindCourse,同時,GetProject這個方法的參數類型為 Project,Project為Course 的基類,所以在第六行代碼中它發生了逆變.

實例 8 : 泛型委托中的逆變

1 public delegate void Find<in T>(T t); 2 Find<Project> getProject = p => Console.Write("查看一個項目"); 3 Find<Course> getCourse = getProject;

相信通過了前面的幾個實例,這個例子也就不難看懂了,在上述的代碼中,我們首先聲明了一個泛型委托,并且泛型中有一個in說明是可以進行逆變,然后在第二行代碼中,我們還是通過lambda表達式,創建一個參數類型為Project的函數,注意第三行代碼, 第三行代碼中將GetProject方法賦值給了getCourse,此處發生了逆變.

【四】.NET中可變性的好處

1 、更好的代碼復用性.

通過剛才的幾個實例,我們可以知道,如果在Project下還有Excerise,Test等派生類的話, 利用協變和逆變性,我們就可以直接?Project.GetCourseByProjects(ExceriseList);?(協變了) . IBase<ExceriseList> exceriseList = new Dervied<Project>(); (逆變了)。所以我們就不需要在去繁多的創建多余的實例對象來調用Project和使用ExceriseList

2、更好的保持了泛型的類型安全性

首先,協變和逆變是通過out,in來指定的,編譯器是不知道那種形式是協變那種形式是逆變的,通過out(輸出參數)和in(輸入參數),來指定參數的輸入輸出類型這一形式,很好的保持了泛型的類型安全性.

PS ?: ref 也是一種,用來指定不變性,指定要求傳入什么類型的就是什么類型,在一般我們開發過程中,通過都是通過這樣的形式來傳參的,比如:

實例 9 : ref雙向傳值,要求實參類型必須與形參類型完全一致

1 Project p = new Project(); 2 GetProject( ref p); 3 public static void GetProject(ref Project project) 4 { 5 Console.WriteLine(project.Name); 6 }

調用方法所傳入的類型必須要與方法要求的參數類型完全一致

【五】總結

平日里我們覺得一些比較難的技術點,當我們花費一些時間去學習,去總結,去思考一下.會發現其實并不是我們想象中那么難, 難得是我們下定決心去做的那份意念而已.

通過本文我們了解到了協變性、逆變性、不變性的定義,以及它是通過一種什么樣的形式來實現的, 另外通過實例我們也可以想到如果用好了它,也會給我的開發帶來事半功倍的效果。使我們的代碼更加優雅、提高程序可擴展性以及復用性,同時這不也是一種多態的體現嗎? ?【劉彬版權所有,如轉載請注明出處.】

通過協變和逆變也有一些限制,這可能也是因為設計者出于類型安全性的方面考慮,它是不支持類的類型參數的可變性,只有接口和委托可以擁有可變的類型參數. 可變性只支持引用轉換.

?

?

??.NET解析系列目錄?

?

轉載于:https://www.cnblogs.com/DeepLearing/p/4592759.html

《新程序員》:云原生和全面數字化實踐50位技術專家共同創作,文字、視頻、音頻交互閱讀

總結

以上是生活随笔為你收集整理的.NET可变性解析(协变和逆变)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。