真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

EffectiveC#之如何使用泛型

這篇文章主要講解了“Effective C#之如何使用泛型”,文中的講解內(nèi)容簡(jiǎn)單清晰,易于學(xué)習(xí)與理解,下面請(qǐng)大家跟著小編的思路慢慢深入,一起來(lái)研究和學(xué)習(xí)“Effective C#之如何使用泛型”吧!

創(chuàng)新互聯(lián)自2013年起,是專(zhuān)業(yè)互聯(lián)網(wǎng)技術(shù)服務(wù)公司,擁有項(xiàng)目成都網(wǎng)站制作、網(wǎng)站設(shè)計(jì)、外貿(mào)網(wǎng)站建設(shè)網(wǎng)站策劃,項(xiàng)目實(shí)施與項(xiàng)目整合能力。我們以讓每一個(gè)夢(mèng)想脫穎而出為使命,1280元溫泉做網(wǎng)站,已為上家服務(wù),為溫泉各地企業(yè)和個(gè)人服務(wù),聯(lián)系電話:18980820575

只定義剛好夠用的約束條件

泛型約束可以規(guī)定一個(gè)泛型類(lèi)必須采用什么樣的類(lèi)型參數(shù)才能夠正常地運(yùn)作。設(shè)定約束條件的時(shí)候,太寬或太嚴(yán)都不合適。 如果根本就不加約束,那么程序必須在運(yùn)行的時(shí)候做很多檢查,并執(zhí)行更多的強(qiáng)制類(lèi)型轉(zhuǎn)換操作。而且在編譯器為這個(gè)泛型類(lèi)型的定義生成IL碼的時(shí)候,通過(guò)約束還可以為提供更多的提示,如果你不給出任何提示,那么編譯器就只好假設(shè)這些類(lèi)型參數(shù)所表示的都是最為基本的System.Object,也就是假設(shè)將來(lái)的實(shí)際類(lèi)型只支持由System.Object所公布的那些方法,這使得凡是沒(méi)有定義在System.Object里面的用法全都會(huì)令編譯器報(bào)錯(cuò),甚至連最為基本的new T()等操作也不支持。

但添加約束的時(shí)候也不要過(guò)分嚴(yán)格,以至于限制了泛型類(lèi)的使用范圍,只添加確實(shí)有必要的約束即可。

創(chuàng)建泛型類(lèi)時(shí),應(yīng)該給實(shí)現(xiàn)了IDisposable的類(lèi)型參數(shù)提供支持

如果在泛型類(lèi)里面根據(jù)類(lèi)型參數(shù)創(chuàng)建了實(shí)例,那么就應(yīng)該判斷該實(shí)例所屬的類(lèi)型是否實(shí)現(xiàn)了IDisposable接口。如果實(shí)現(xiàn)了,就必須編寫(xiě)相關(guān)的代碼,以防程序在離開(kāi)泛型類(lèi)之后發(fā)生資源泄漏。這還要分不同的情況: 泛型類(lèi)的方法根據(jù)類(lèi)型參數(shù)所表示的類(lèi)型來(lái)創(chuàng)建實(shí)例并使用該實(shí)例類(lèi)似下面的寫(xiě)法,如果T是非托管資源,那么就會(huì)造成內(nèi)存泄露:

public interface IEngine
{
  void DoWork();
}

public class EngineDriver where T : IEngine, new()
{
  public void GetThingsDone()
  {
    var driver =new T();
    driver.DoWork();
  }
}

正確的寫(xiě)法應(yīng)該是:

var driver =new T();
using (driver as IDisposable)
{
  driver.DoWork();
}

編譯器會(huì)把driver視為IDisposable,并創(chuàng)建隱藏的局部變量,用以保存指向這個(gè)IDisposable的引用。在T沒(méi)有實(shí)現(xiàn)IDisposable的情況下,這個(gè)局部變量的值是null,此時(shí)編譯器不調(diào)用Dispose(),因?yàn)樗谡{(diào)用之前會(huì)先做檢查。反之,如果T實(shí)現(xiàn)了IDisposable,那么編譯器會(huì)生成相應(yīng)的代碼,以便在程序退出using塊的時(shí)候調(diào)用Dispose()方法。 這段代碼等同于:

var a = driver as IDisposable;
driver.DoWork();
a?.Dispose();

使用using后,需要注意的是所有調(diào)用driver實(shí)例的操作都不可以放在using區(qū)域之后,因?yàn)槟菚r(shí)driver已經(jīng)被釋放了。

泛型類(lèi)將根據(jù)類(lèi)型參數(shù)所創(chuàng)建的那個(gè)實(shí)例當(dāng)作成員變量在這種情況下,那么代碼會(huì)復(fù)雜一些。該類(lèi)擁有的這個(gè)引用所指向的對(duì)象類(lèi)型可能實(shí)現(xiàn)了IDisposable接口,也可能沒(méi)有實(shí)現(xiàn),但為了應(yīng)對(duì)可能實(shí)現(xiàn)了IDisposable接口的情況,泛型類(lèi)本身就必須實(shí)現(xiàn)IDisposable,并且要判斷相關(guān)的資源是否實(shí)現(xiàn)了這個(gè)接口,如果實(shí)現(xiàn)了,就要調(diào)用該資源的Dispose()方法。

public class EngineDriver2 : IDisposable where T : IEngine, new()
{
  // it's expensive to create, so create to null
  private Lazy driver = new Lazy(() => new T());
  public void GetThingsDone() => driver.Value.DoWork();

  public void Dispose()
  {
    if (driver.IsValueCreated)
    {
      var resource = driver.Value as IDisposable;
      resource?.Dispose();
    }
  }
}

或者可以將driver的所有權(quán)轉(zhuǎn)移到該類(lèi)之外,于是也就不用關(guān)心資源的釋放了。|

public sealed class EngineDriver3 where T : IEngine
{
  private T driver;

  public EngineDriver3(T driver)
  {
    this.driver = driver;
  }
}

如果有泛型方法,就不要再創(chuàng)建針對(duì)基類(lèi)或接口的重載版本

如果有多個(gè)相互重載的方法,那么編譯器就需要判斷哪一個(gè)方法應(yīng)該得到調(diào)用。而在引入泛型方法之后,這套判斷規(guī)則會(huì)變得更加復(fù)雜,因?yàn)橹灰軌蛱鎿Q其中的類(lèi)型參數(shù),就可以與這個(gè)泛型方法相匹配。 比如有下面三個(gè)類(lèi)型,它們之間的關(guān)系如代碼所示:

public class MyBase
{
}

public interface IMsgWriter
{
  void WriteMsg();
}

public class MyDerived : MyBase, IMsgWriter
{
  void IMsgWriter.WriteMsg() => Console.WriteLine("Inside MyDerived.WriteMsg");
}

接下來(lái)定義三個(gè)重載方法,其中包括了泛型方法:

static void WriteMsg(MyBase b)
{
  Console.WriteLine("Inside WriteMsg(MyBase b)");
}

static void WriteMsg(T obj)
{
  Console.WriteLine("Inside WriteMsg(T obj)");
}

static void WriteMsg(IMsgWriter obj)
{
  Console.Write("Inside WriteMsg(IMsgWriter obj)");
}

那么如下三種調(diào)用寫(xiě)法,結(jié)果是怎樣的呢?

MyDerived derived = new MyDerived();
WriteMsg(derived);

var msgWriter = derived as IMsgWriter;
WriteMsg(msgWriter);

var mbase = derived as MyBase;
WriteMsg(mbase);

下面為運(yùn)行結(jié)果,與你預(yù)想是否一致呢?

Inside WriteMsg(T obj)
Inside WriteMsg(IMsgWriter obj)
Inside WriteMsg(MyBase b)

第一條結(jié)果表明了一個(gè)極為重要的現(xiàn)象:如果對(duì)象所屬的類(lèi)繼承自基類(lèi)MyBase,那么以該對(duì)象為參數(shù)來(lái)調(diào)用WriteMsg時(shí),WriteMsg總是會(huì)先于WriteMsg(MyBase b)而得到匹配,這是因?yàn)槿绻c泛型版的方法相匹配,那么編譯器可以直接把子類(lèi)MyDerived視為其中的類(lèi)型參數(shù)T,但若要與基類(lèi)版的方法相匹配,則必須將MyDerived型的對(duì)象隱式地轉(zhuǎn)換成MyBase型的對(duì)象,所以,它認(rèn)為泛型版的WriteMsg更好。 如果要調(diào)用到WriteMsg(MyBase b), 需要將MyDerived型的對(duì)象顯式地轉(zhuǎn)換成MyBase型對(duì)象,就像第三條測(cè)試那樣。

如果不需要把類(lèi)型參數(shù)所表示的對(duì)象設(shè)為實(shí)例字段,那么應(yīng)該優(yōu)先考慮創(chuàng)建泛型方法,而不是泛型類(lèi)

一般來(lái)說(shuō),我們通常的習(xí)慣是定義泛型類(lèi),但有時(shí)更推薦用泛型方法。因?yàn)槭褂梅盒头椒〞r(shí)所提供的泛型參數(shù)只需與該方法的要求相符即可,而使用泛型類(lèi)時(shí)所提供的泛型參數(shù)則必須滿足該類(lèi)所定義的每一條約束。如果將來(lái)還要給類(lèi)里面添加代碼,那么可能會(huì)對(duì)類(lèi)級(jí)別的泛型參數(shù)施加更多的約束,從而令該類(lèi)的適用場(chǎng)景變得越來(lái)越窄。

此外,泛型方法相比泛型類(lèi)會(huì)更加靈活,比如下面的泛型工具類(lèi)獲取提供了獲取較大值的方法:

public class Utils
{
  public static T Max(T left, T right)
  {
    return Comparer.Default.Compare(left, right) > 0 ? left : right;
  }
}

因?yàn)槭欠盒?,那么每次調(diào)用都要提供類(lèi)型:

Utils.Max("c", "d");
Utils.Max(4, 3);

這樣雖然類(lèi)本身的實(shí)現(xiàn)比較方便,但調(diào)用端使用起來(lái)卻比較麻煩,更重要的是,值類(lèi)型可以直接使用Math.Max,而不需要每次都讓程序在運(yùn)行的時(shí)候先去判斷相關(guān)類(lèi)型是否實(shí)現(xiàn)了IComparer,然后才能調(diào)用合適的方法,Math.Max可以提供更好的性能,所以可以改進(jìn)為對(duì)于值類(lèi)型提供不同版本的Max方法:

public class Utils1
{
  public static T Max(T left, T right)
  {
    return Comparer.Default.Compare(left, right) > 0 ? left : right;
  }

  public static int Max(int left, int right)
  {
    return Math.Max(left, right) > 0 ? left : right;
  }
  
  public static double Max(double left, double right)
  {
    return Math.Max(left, right) > 0 ? left : right;
  }
}

經(jīng)過(guò)這樣的修改,將泛型類(lèi)改成了部分使用泛型方法,對(duì)于int、double,編譯器會(huì)直接調(diào)用非泛型的版本,其它的類(lèi)型會(huì)匹配到泛型版本。

Utils1.Max("c", "d");
Utils1.Max(4, 3);

這樣寫(xiě)還有個(gè)好處是,將來(lái)如果又添加了一些針對(duì)其他類(lèi)型的具體版本,那么編譯器在處理那些類(lèi)型的參數(shù)時(shí)就不會(huì)去調(diào)用泛型版本,而是會(huì)直接調(diào)用與之相應(yīng)的具體版本。

但也要注意的是,并非每一種泛型算法都能夠繞開(kāi)泛型類(lèi)而單純以泛型方法的形式得以實(shí)現(xiàn)。有兩種情況,必須把類(lèi)寫(xiě)成泛型類(lèi):

  1. 該類(lèi)需要將某個(gè)值用作其內(nèi)部狀態(tài),而該值的類(lèi)型必須以泛型來(lái)表達(dá)(例如集合類(lèi))

  2. 該類(lèi)需要實(shí)現(xiàn)泛型版的接口。

除此之外的其他情況通常都可以考慮用包含泛型方法的非泛型來(lái)實(shí)現(xiàn)。

只把必備的契約定義在接口中,把其他功能留給擴(kuò)展方法去實(shí)現(xiàn)

如果程序中有很多個(gè)類(lèi)都必須實(shí)現(xiàn)所要設(shè)計(jì)的某個(gè)接口,那么定義接口的時(shí)候就應(yīng)該定義盡量少的方法,后續(xù)可以采用擴(kuò)展方法的形式編寫(xiě)一些針對(duì)該接口的便捷方法。這樣做不僅可以使實(shí)現(xiàn)接口的人少寫(xiě)一些代碼,而且可以令使用接口的人能夠充分利用那些擴(kuò)展方法。

但使用擴(kuò)展方法時(shí)需要注意一點(diǎn):如果已經(jīng)針對(duì)某個(gè)接口定義了擴(kuò)展方法,而其他一些類(lèi)又想要以它們自己的方式來(lái)實(shí)現(xiàn)這個(gè)同名方法,那么擴(kuò)展方法就會(huì)被覆蓋,類(lèi)似下面這樣,針對(duì)IFoo定義了擴(kuò)展方法NextMarker,同時(shí)也在MyType中實(shí)現(xiàn)了NextMarker。

public interface IFoo
{
  int Marker { get; set; }
}

public static class FooExtension
{
  public static void NextMarker(this IFoo foo)
  {
    foo.Marker++;
  }
}

public class MyType: IFoo
{
  public int Marker { get; set; }

  public void NextMarker()
  {
    this.Marker += 5;
  }
}

那么下面代碼的結(jié)果就是5,而不是1

var myType =new MyType();
myType.NextMarker();
Console.WriteLine(myType.Marker);  // 5

而如果需要調(diào)用擴(kuò)展方法,需要顯示地將myType轉(zhuǎn)換為IFoo。

var myType =new MyType();
var a = myType as IFoo;
a.NextMarker();

感謝各位的閱讀,以上就是“Effective C#之如何使用泛型”的內(nèi)容了,經(jīng)過(guò)本文的學(xué)習(xí)后,相信大家對(duì)Effective C#之如何使用泛型這一問(wèn)題有了更深刻的體會(huì),具體使用情況還需要大家實(shí)踐驗(yàn)證。這里是創(chuàng)新互聯(lián),小編將為大家推送更多相關(guān)知識(shí)點(diǎn)的文章,歡迎關(guān)注!


網(wǎng)站題目:EffectiveC#之如何使用泛型
本文路徑:http://weahome.cn/article/jphoeg.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部