泛型的界定和可空类型的简便表明,NET面试题体

07.C#泛型的限量和可空类型的简约表达(三章3.5-四章4.1),

小分享:作者有几张Ali云优惠券,用券购买也许升级Ali云相应产物最多能够巨惠五折!领券地址:

< hr />

  本身在写随笔的还要,也是在上学,对于书中的语句超级多实在未有太好的精通,读一本书,要消食!!!三章都以讲泛型的,最后写一下泛型的节制,对于本章学习的终止,one end,one begin.

  看上面包车型地铁代码

 1 public class Teacher
 2     {
 3         public enum Flag { Chinese, English };
 4         public string Name { get; set; }
 5     }
 6 
 7     public class ChineseTeacher : Teacher
 8     {
 9         Flag f = Flag.Chinese;
10     }
11 
12     public class EnglishTeacher : Teacher
13     {
14         Flag f = Flag.English;
15     }

  先来讲下可变性,可变性分为协变性和逆变性。个人轻便地领悟为协变性成效于再次回到类型时,重临的目的相符具名中的重回类型,并伸开辟展调换,如三个方式再次来到二个Teacher对象,此时我们能够重回二个EnglishTeacher对象,因为EnglishTeacher类是从Teacher类世襲的。或许签名阐明要重回三个接口,则足以回到三个落到实处该接口的类的实例。而对于逆变性,则效果于参数的转递,如若二个形式接受三个Teacher对象作为参数,那能够流传叁个ChineseTeacher对象。Teacher说自家低头了,你能够流传一个落成只怕再三再四小编的就可以,不知明白是不是科学,请指正。

  1. 说下泛型吧,泛型不帮助协变,如上边的代码将出错

1 List<Teacher> list = new List<ChineseTeacher> {
2                 new ChineseTeacher(),
3                 new EnglishTeacher()
4             };

  list对象指向的是二个List<Teacher>列表,而在其实new的时候针没有错是List<ChineseTeacher>列表,List<ChineseTeacher>到List<Teacher>,不驾驭能不能了然为协变?请指正。向八个List<ChineseTeacher>到场一个ChineseTeacher实例料定是没有错的,那向List<Teacher>参预多少个EnglishTeacher对象自然也是对的的,但向List<ChineseTeacher>参与三个EnglishTeacher对象自然是异形,所以从上述的多义性能够见见泛型不帮忙协变性。

  2.缺乏操作符约束如故"数值"限制

  如大家在一个泛型方法中希望对泛型类实例进行操作符的运算是不起成效,且有BUG,在编写翻译时则报错,而后续可以运用动态类型跳过编写翻译阶段,等到执行阶段再作决策。


  鉴于只说泛型可以小说篇幅过短,当然也知道小说在精不在多,说下并未有援引可空类型前,大家是哪些表示贰个"未有值"的值。

  1.扬弃一个值,用那一个值表示"未有值",多个人支付时,就象是钦赐errorCode:1象克制务器出错、errorCode:2代表客户不正确操作。

  2.应用二个装修引用类型包涵叁个值,设置多个bool类型的标识

  3.与值属性绝没有错踏入个bool类型的标志,与2的不等在于,3的办法在于在同叁个类中,而2则是将值和符号封装成类,在使用值的类中加入密封后类的实例。

   二种方法的弱项简单的说,输入过多的代码,无法重复使用,基于这几个C#引进了可空类型。

 

  请斧正。

 

参照页面:

小分享:作者有几张Ali云促销券,用券购买恐怕进级Ali云相应付加物最多能够减价...

“**可变性是以风流洒脱连串型安全的方式,将一个指标作为另一个目的来选取。“

  • Jon Skeet**

.NET面试题种类目录

  • .NET面试题连串[1] - .NET框架底子知识(1) 
  • .NET面试题类别[2] - .NET框架根底知识(2) 
  • .NET面试题连串[3] - C# 基本功知识(1)
  • .NET面试题连串[4] - C# 功底知识(2)
  • .NET面试题类别[5] - 垃圾回笼:概念与计策
  • .NET面试题类别[6] - 反射
  • .NET面试题类别[7] - 委托与事件

泛型

泛型比较反射,委托等比较抽象的定义要更接地气得多,並且在平凡职业时,大家大致每日都和泛型有接触。大多数人对泛型都以相比熟谙的。

泛型集结是体系安全的集结。相对于泛型System.Collections.Generic,大家有档案的次序不安全的集结System.Collections,当中的成员均为Object类型。三个经文的例子是ArrayList。

在运用ArrayList时,大家可以插入大肆等级次序的多少,若是插入值类型的多少,其都会装箱为Object类型。那引致类型不安全,大家不精通抽取的数据是否想要的种类。泛型(集合卡塔尔的数据类型是联合的,是项目安全的,未有装箱和拆箱难题,提供了更加好的品质。为泛型变量设置暗中认可值时常使用default关键字张开:T temp = default(T)。借使T为援用类型,则temp为null,若是T为值类型,则temp为0。

ArrayList的泛型集结版本为List<T>。T称为品种参数。调用时钦赐的求实项目叫加强际参数(实参卡塔 尔(阿拉伯语:قطر‎。

面试必需精晓的泛型三大益处:类型安全,增强质量,代码复用。

泛型集结的施用机缘:大约任何时候,都不思虑不用泛型群集替代泛型集结。非常多非泛型集结也会有了团结的泛型版本,举例栈,队列等。

泛型方法

泛型方法的行使时机日常为流传类型或然有众种种,但管理格局却同样的水浇地。那时候大家得以无需写很三个重载,而思考用泛型方法达到代码复用的目标。协作泛型约束,能够写出更谨严的法子。泛型委托也能够看作是泛型方法的大器晚成种接受。

例如说调换几个同品种变量的值:

static void Swap<T>(ref T lhs, ref T rhs)
{
    T temp;
    temp = lhs;
    lhs = rhs;
    rhs = temp;
}

泛型限定

自律的效果是限量能钦定成泛型实参(即T的维妙维肖项目卡塔尔国的数码。通过节制类型的数码,能够对这个项目实施越多的操作。比方上边包车型大巴措施,T被封锁为必需是兑现了IComparable接口的种类。那时,传入的T除了全部object类型的不二秘诀之外,还附扩大了一个CompareTo方法。由于保管了流传的T必需是促成了IComparable接口的门类,就能够一定T类型一定带有CompareTo方法。假诺去掉节制,o1是从未有过CompareTo方法的。

static int Compare<T>(T o1, T o2) where T : IComparable<T>
{
     return o1.CompareTo(o2);
}

那会儿只要将object类型的数额传入方法,则会报错。因为object未有兑现IComparable<T>接口。

泛型限制分为如下几类:

  • 接口节制:泛型实参必得兑现有个别接口。接口约束能够有四个。
  • 基类型节制:泛型实参必须是某些基类的派生类。特别的,能够钦定T : class / T : struct,此时T分别只可以为援用类型或值类型。基类型节制必得放在其余节制在此以前。
  • 构造函数new()节制:泛型实参必得有所可访谈的无参数构造函数(默许的也可卡塔 尔(阿拉伯语:قطر‎。new()约束出未来where子句的结尾。

假使泛型方法未有其余自律,则传出的靶子会被视为object。它们的效劳比较有限。不可能采用 != 和 == 运算符,因为不能够确认保证具体项目参数能支撑这一个运算符。

协变和逆变

可变性是以一种类型安全的章程,将多少个指标作为另多个目的来使用。其相应的术语则是不改变性(invariant卡塔尔国。

可变性

可变性是以一连串型安全的法子,将二个目的作为另一个目标来行使。诸如对平常世袭中的可变性:若某方法注解重临类型为Stream,在促成时方可回到三个MemoryStream。可变性有两类别型:协变和逆变。

协变性:能够创建三个相比相像项指标变量,然后为其赋值,值是贰个比较特殊类别的变量。比方:

string str = "test";
// An object of a more derived type is assigned to an object of a less derived type. 
object obj = str;

因为string分明是多个object,所以那样的成形特别不荒谬。 

逆变性:在地点的例证中,大家不能够将str和二个新的object对象画等号。借使强行要落到实处的话,只可以如此干:

string s = (string) new object();

但那样依然会在运作时出错。那也告诉我们,逆变性是特别不健康的。

 

泛型的协变与逆变

协变性和out关键字搭配使用,用于向调用者重临某项操作的值。譬如上面包车型客车接口只有一个主意,正是生育一个T类型的实例。那么大家得以流传叁个特定类型。如大家能够将IFactory<Pizza>视为IFactory<Food>。那也适用于Food的拥有子类型。(将要其就是七个更雷同品种的兑现卡塔尔

    interface IFactory<T>
    {
        T CreateInstance();
    }

逆变性则相反,和in关键字搭配使用,指的是API将会费用值,并不是临盆值。这个时候貌似项目出今后参数中:

    interface IPrint<T>
    {
        void Print(T value);
    }

那象征纵然我们落实了IPrint<Code>,我们就足以将其看作IPrint<CsharpCode>使用。(将要其视为一个更具象品种的兑现卡塔 尔(阿拉伯语:قطر‎

意气风发经存在双向的传递,则什么也不会发生。那系列型是不改变体(invariant)。

     interface IStorage<T>
    {
        byte[] Serialize(T value);
        T Deserialize(byte[] data);
    }

以此接口是不变体。大家不能够将它便是三个更具体或更相同品种的达成。

假诺好似下世袭关系People –> Teacher,People –> Student。

生机勃勃旦大家以协变的主意利用(假让你建设构造了一个IStorage< Teacher >的实例,并将其视为IStorage<People>卡塔 尔(英语:State of Qatar)则大家恐怕会在调用Serialize时发生万分,因为Serialize方法不帮助协变(即使参数是People的任何子类,举例Student,则IStorage< Teacher >将不可能类别化Student卡塔尔国。

假诺大家以逆变的主意利用(借使你营造了贰个IStorage<People>的实例,并将其视为IStorage< Teacher >卡塔 尔(英语:State of Qatar),则大家兴许会在调用Deserialize时产生非常,因为Deserialize方法不帮忙逆变,它不能不回到People不能够回去Teacher。

行使in和out代表可变性

设若类型参数用于出口,就应用out,假若用于输入,就利用in。注意,协变和逆变性体未来泛型类T和T的派生类。近来out 和in 关键字只可以在接口和委托中央银行使。

IEnumerable<out T>帮衬协变性

IEnumerable<T>支持协变性,它同意多个相像上面具名

void 方法(IEnumerable<T> anIEnumberable)

的艺术,该措施传入更具体的品种(T的派生类卡塔 尔(阿拉伯语:قطر‎,但在形式内部,类型会被充当IEnumerable<T>。注意out关键字。

上边的事例演示了协变性。大家运用IEnumerable<T>的协变性,传入较为具体的项目Circle。编写翻译器会将其视作较为抽象的品种Shape。

    public class Program
    {
        public static void Main(string[] args)
        {
            var circles = new List<Circle>
            {
                new Circle(new Point(0, 0), 15),
                new Circle(new Point(10, 5), 20),
            };
            var list = new List<IShape>();

            //泛型的协变:
            //AddRange传入的是特殊的类型List<Circle>,但要求是一般的类型List<IShape>
            //AddRange方法签名:void AddRange(IEnumerable<T> collection)
            //IEnumerable<out T>允许协变(对于LINQ来说,协变尤其重要,因为很多API都表示为IEnumerable<T>)
            list.AddRange(circles);

            //C# 4.0之前只能这么做
            list.AddRange(circles.Cast<IShape>());
        }
    }

    public sealed class Circle : IShape
    {
        private readonly Point center;
        public Point Center { get { return center; } }

        private readonly double radius;
        public double Radius { get { return radius; } }

        public Circle(Point center, int radius)
        {
            this.center = center;
            this.radius = radius;
        }

        public double Area
        {
            get { return Math.PI * radius * radius; }
        }
    }

    public interface IShape
    {
        double Area { get; }
    }

IComparer<in T>支持逆变性

IComparer扶助逆变性。大家能够归纳的落实叁个足以相比任何图形面积的措施,传入的输入类型(in卡塔 尔(阿拉伯语:قطر‎是最General的项目IShape。之后,在利用时,我们得到的结果是相比较具体的花色Circle。因为其余图形都足以相比面积,圆形当然也足以。

只顾IComparer的具名是public interface IComparer<in T>。

    public class Program
    {
        public static void Main(string[] args)
        {
            var circles = new List<Circle>
            {
                new Circle(new Point(0, 0), 15),
                new Circle(new Point(10, 5), 20),
            };

            //泛型的逆变:
            //AreaComparer可以比较任意图形的面积,但我们可以传入具体的图形例如圆或正方形
                //Compare方法签名:Compare(IShape x, IShape y)
            //IComparer<in T>支持逆变
            //传入的是圆形Circle,但要求的输入是IShape
            circles.Sort(new AreaComparer());
        }
    }

    class AreaComparer : IComparer<IShape>
    {
        public int Compare(IShape x, IShape y)
        {
            return x.Area.CompareTo(y.Area);
        }
    }

C#中泛型可变性的界定

1. 不协助类的等级次序参数的可变性。独有接口和委托能够有所可变的品类参数。in out 修饰符只可以用来修饰泛型接口和泛型委托。

2. 可变性只协理援引调换。可变性只好用于援用类型,禁止其它值类型和用户定义的变换,如上边的转换是不行的:

  • 将 IEnumerable<int> 转变为 IEnumerable<object> ——装箱调换
  • 将 IEnumerable<short> 转变为 IEnumerable<int> ——值类型调换
  • 将 IEnumerable<string> 调换为 IEnumerable<XName> ——顾客定义的转换
  1. 类型参数使用了 out 可能 ref 将防止可变性。对于泛型类型参数来说,假设要将该项指标实参传给使用 out 或然 ref 关键字的法子,便不允许可变性,如:

    delegate void someDelegate(ref T t)

这段代码编写翻译器会报错。

4. 可变性必得显式钦定。从贯彻上来讲编译器完全能够和谐剖断什么泛型参数能够逆变和协变,但实质上却未曾那样做,那是因为C#的支出团队认为:必得由开荒者明确的钦点可变性,因为那会促使开拓者思忖他们的表现将会推动哪些结果,从而构思他们的规划是或不是站得住。

  1. 多播委托与可变性不可能混用。下边包车型地铁代码能够通过编写翻译,不过在运维时会抛出 ArgumentException 非凡:

    Func stringFunc = () => ""; Func objectFunc = () => new object(); Func combined = objectFunc + stringFunc;

    这是因为承担链接七个委托的 Delegate.Combine方法供给参数必得为相像的门类,而地点的多个泛型委托的输出叁个为字符串,另多个为object。上边的以身作则大家得以修改成如下准确的代码:

    Func<string> stringFunc = () => "";
    Func<object> defensiveCopy = new Func<object>(stringFunc);
    Func<object> objectFunc = () => new object();
    Func<object> combined = objectFunc + defensiveCopy;
    

    当时三个泛型委托的出口均为object。

     

    协变与逆变的相互影响

    以下的代码中,接口IBar中有三个办法,其收受另三个接口IFoo作为参数。IFoo是永葆协变的。那样会冒出三个主题素材。

        interface IFoo<in T>
        {
    
        }
    
        interface IBar<in T>
        {
            void Test(IFoo<T> foo);
        }
    

    假如T为字符串类型。则只要有意气风发类Bar <T>: IBar<T>,另后生可畏类Foo<T>:IFoo<T>,则Bar的某些实例应该能够如此调用方法:aBar.Test (foo)。

        class Bar<T> : IBar<T>
        {
            public void Test(IFoo<T> foo)
            {
                throw new NotImplementedException();
            }
        }
    
        class Foo<T> : IFoo<T>
        {
    
        }
    
        class Program
        {
            public static void Main()
            {
                Bar<string> aBar = new Bar<string>();
                Foo<object> foo = new Foo<object>();
                aBar.Test(foo);
            }
        }
    

    当调用方法之后,传入的参数类型是Foo<object>。大家再看看方法的具名:

        interface IBar<in T>
        {
            void Test(IFoo<T> foo);
        }
    

    于今大家的aBar的门类参数T是string,所以,大家意在的Test方法的散播类型也理应是IFoo<string>,只怕能够转移成IFoo<string>的花色,但传播的却是二个object。所以,那多少个接口的点子的写法是不平时的。

        interface IFoo<out T>
        {
    
        }
    

    当把IFoo接口的签名改用out修饰之后,难点就缓慢解决了。当时由于允许逆变,Foo<object>就可以转移成IFoo<string>了。但是小编目光如豆,近些日子尚未觉察那一个特点在其实职业中有如何应用。

    仿照效法资料

    本文由华夏彩票发布于关于计算机,转载请注明出处:泛型的界定和可空类型的简便表明,NET面试题体

    您可能还会对下面的文章感兴趣: