C# 8.0 默认接口实现
C# 8.0 默認(rèn)接口實(shí)現(xiàn)
Intro
C# 8.0 開始引入了默認(rèn)接口實(shí)現(xiàn),也就是可以在接口里寫方法實(shí)現(xiàn)。
在之前的版本中接口上是沒有辦法定義實(shí)現(xiàn)的,方法也都是 public 的,除了接口和屬性之外是不能定義其他數(shù)據(jù)的,這也意味著,接口從一開始就要設(shè)計(jì)得比較好,否則在已有接口里增加新方法的時(shí)候其實(shí)現(xiàn)就必須要修改,否則就會(huì)編譯失敗,默認(rèn)接口實(shí)現(xiàn)使得可以不造成破壞性變更的前提下在接口中新增加方法,只需要在接口中提供一個(gè)默認(rèn)的實(shí)現(xiàn)即可。
Sample
下面我們來看一個(gè)示例吧:
internal?interface?IFly {string?Name?{?get;?} } internal?class?Superman?:?IFly {public?string?Name?=>?nameof(Superman); } internal?class?MonkeyKing?:?IFly {public?string?Name?=>?nameof(MonkeyKing); }這是一個(gè)基本的接口定義,并提供了兩個(gè)實(shí)現(xiàn),緊接著我們來為接口新增一個(gè)方法,
internal?interface?IFly {string?Name?{?get;?}void?Fly()?=>?Console.WriteLine($"{Name.GetValueOrDefault((DefaultName))}?is?flying"); }internal?class?Superman?:?IFly {public?string?Name?=>?nameof(Superman);public?void?Test(){((IFly)?this).Fly();Console.WriteLine(Name);} }internal?class?MonkeyKing?:?IFly {public?string?Name?=>?nameof(MonkeyKing);public?void?Fly(){Console.WriteLine($"I'm?{Name},?I'm?flying");} }我們?cè)诮涌诶镌黾恿艘粋€(gè) Fly 方法,并提供了一個(gè)默認(rèn)實(shí)現(xiàn),在其中一個(gè)實(shí)現(xiàn)中進(jìn)行了重寫,我們來寫一段代碼測試一下吧
//?Cannot?resolve?symbol?'Fly' //?new?Superman().Fly();IFly?fly?=?new?Superman(); fly.Fly();fly?=?new?MonkeyKing(); fly.Fly();輸出結(jié)果如下:
Superman?is?flying I'm?MonkeyKing,?I'm?flying IFly上面的示例中 Superman 沒有定義 Fly 這個(gè)方法,是不能直接調(diào)用 Fly 方法的,需要先轉(zhuǎn)成 IFly 接口然后再調(diào)用,此時(shí)方法實(shí)現(xiàn)是在接口里定義的邏輯,而 MonkeyKing 實(shí)現(xiàn)了 Fly 方法,所以會(huì)使用它自己的 Fly 實(shí)現(xiàn),如上面所示。
除了上面的基本用法之外,現(xiàn)在可以在接口里定義靜態(tài)字段靜態(tài)方法來實(shí)現(xiàn)更好的方法復(fù)用,我們?cè)谏厦娴氖纠镅菔疽幌?#xff0c;修改后的示例如下:
internal?interface?IFly {private?const?string?DefaultName?=?nameof(IFly);protected?static?string?GetDefaultName()?=>?DefaultName;public?static?string?GetPublicName()?=>?DefaultName;//?Interface?cannot?contain?instance?fields//?private?string?name?=?"";string?Name?{?get;?}void?Fly()?=>?Console.WriteLine($"{Name.GetValueOrDefault((DefaultName))}?is?flying"); }internal?class?MonkeyKing?:?IFly {public?string?Name?=>?nameof(MonkeyKing);public?void?Fly(){Console.WriteLine($"I'm?{Name},?I'm?flying,?DefaultName:{IFly.GetDefaultName()}");} }如果定義了 protected static 的方法或字段,則在實(shí)現(xiàn)接口的類中就可以通過 IFly.GetDefaultName() 來調(diào)用接口中的方法了,如果是 protected 就只能在實(shí)現(xiàn)它的類型中使用,如果要在沒有實(shí)現(xiàn)接口的類型中調(diào)用可以聲明為 public 就可以了,下面是在沒有實(shí)現(xiàn)接口的類型中調(diào)用的示例:
//?Cannot?access?protected?method?'GetDefaultName'?here //?IFly.GetDefaultName().Dump();IFly.GetPublicName().Dump();More
雖然現(xiàn)在可以這樣用,但我個(gè)人還是推薦沿用之前的接口用法,不要輕易使用這個(gè)特性,提前設(shè)計(jì)提前規(guī)劃才是正道,不要想著事后補(bǔ)償,感覺這個(gè)特性比較合適的一個(gè)使用場景是現(xiàn)在基于接口的擴(kuò)展方法,擴(kuò)展方法作為一個(gè)接口的默認(rèn)實(shí)現(xiàn),具體類可以重寫這個(gè)實(shí)現(xiàn),使用示例如下:
Task<bool>?SaveProperties(int?id,?Dictionary<string,?object>?properties) {if?(properties?is?null?||?properties.Count?==?0)?return?Task.FromResult(false);var?json?=?JsonConvert.SerializeObject(properties.Select(p?=>?new?PropertyModel(){PropertyName?=?p.Key,PropertyValue?=?p.Value?.ToString()}));return?SaveProperties(id,?json); }Task<bool>?SaveProperties(int?id,?string?properties);在之前的版本中,我一般都是把上面的方法作為一個(gè)擴(kuò)展方法來用,有個(gè)默認(rèn)接口實(shí)現(xiàn)之后也可以考慮加一個(gè)默認(rèn)實(shí)現(xiàn)(僅限于業(yè)務(wù)代碼中,針對(duì)類庫代碼,感覺還是越干凈越好)
References
- https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/tutorials/default-interface-methods-versions 
- https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-8#default-interface-methods 
- https://github.com/WeihanLi/SamplesInPractice/blob/master/CSharp9Sample/DefaultInterfaceImplement.cs 
總結(jié)
以上是生活随笔為你收集整理的C# 8.0 默认接口实现的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: 实现多租户系统的一点思考
- 下一篇: P6砖家:对不起,我没.NET5高并发经
