C#中的泛型实践

98 阅读6分钟

这是一个困扰我很久的问题,到现在都还没有很清楚,弄明白。但我试着做一个小小的实践实验,并询问了AI,如果有人理解很好,可以给我留言,指导一下,非常感谢。

一定义类

泛基类代码

  public class BaseClass<T> where T : BaseClass<T>, new()
    { 
        //在这个类中表示 T一定会有Age 属性,否则不可使用
        public int Age { get; set; }
        public virtual void Test()
        {
            Console.WriteLine("我是基类方法");
        }
    }

派生类代码一

  public class BaseClass:BaseClass<BaseClass>
    {
        //这里 BaseClass 拥有 BaseClass<T> 类的属性和方法,BaseClass是派生类与BaseClass<BaseClass>是基类
        public int Id { get; set; }
        public string Name { get; set; }    

        public string Description { get; set; }

        public override void Test() //重写方法
        {
            Console.WriteLine("我也重写Test方法");
            base.Test();
        }
        public BaseClass() 
        { 
        

        }

        public  virtual void RunTest()
        {
            Console.WriteLine($"id:{Id},名字:{Name},描述:{Description}");
        }

    }

派生类代码二

    public class SonClass:BaseClass<SonClass>
    {
        //这里 SonClass 拥有 BaseClass<T> 类的属性和方法
        public string Address { get; set; }

        public override void Test() //重写方法
        {
           
           // base.Test(); 可以调用,也可以不调用
            Console.WriteLine("这是我重写的自己的方法");
        }
    }

其他泛型类代码

  public class Pair<TFirst, TSecond>
    {

        public TFirst First { get; }
        public TSecond Second { get; }
        public int Thrit { get; }

        public Pair(TFirst first, TSecond second) =>
            (First, Second) = (first, second); //类似于js的SE6 解构表达式

        public Pair(TFirst first, TSecond second,int th)
        {
            First = first;
            this.Second = second;
            this.Thrit = th;
        }

        
    }

独立类代码

    public class Person
    {
        public string FirstName { get; set; }

        public string LastName { get; set; }

        [field: NonSerialized]
        public int Id { get; set; }

        public string FullName => $"{FirstName} {LastName}";

        public Person() { }
    }

二 测试

测试代码

   SonClass son=new SonClass();
            son.Address = "127.0.0.1";
            son.Age = 1;
            son.Test();
            Console.WriteLine($"年龄{son.Age} ");
            Console.WriteLine($"地址{son.Address} ");
            Console.WriteLine("================分割线1================");
            BaseClass baseT = new BaseClass();
            baseT.Test();
  
            baseT.Name = "Test";
            baseT.Age = 1;
            baseT.Id = 1000;
            baseT.Description = "我是非泛型类的属性";
            baseT.RunTest();
            Console.WriteLine("================分割线=========分割线================");
            BaseClass<SonClass> t=new BaseClass<SonClass>();
            t.Age = 100;
            t.Test();
            Console.WriteLine(t.Age);
           // BaseClass<Person> t2 = new BaseClass<Person>(); 这个不行
            Console.WriteLine("================分割线=========分割线================");

            Person a1=new Person();
            a1.FirstName = "张";
            a1.LastName = "Test";
            Person a2 = new Person();
            a2.FirstName= "王";
            a2.LastName = "Test刚";
            Pair<string, string> pair = new Pair<string, string>("a","b");
            Console.WriteLine(pair.First);
            Console.WriteLine(pair.Second);
            Pair<string, string> pair2 = new Pair<string, string>("a", "b",100);
            Console.WriteLine("================分割线1================");
     
            Console.WriteLine(pair2.First);
            Console.WriteLine(pair2.Second);
            Console.WriteLine(pair2.Thrit);
            Pair<Person, Person> pair3 =new  Pair<Person, Person>(a1,a2);
            Console.WriteLine("================分割线2================");
            Console.WriteLine(pair3.First.FullName);
            Console.WriteLine(pair3.Second.FullName);

            Pair<Person, Person> pair4 = new Pair<Person, Person>(a1, a2,500);
            Console.WriteLine("================分割线3================");
            Console.WriteLine(pair4.First.FullName);
            Console.WriteLine(pair4.Second.FullName);
            Console.WriteLine(pair4.Thrit);

三 测试结果

image.png

四理解

五 AI 解释

如何理解下面代码

 //代码 一
public class BaseClass<T> where T : BaseClass<T>, new() { } 
 //代码二
 public class BaseClass:BaseClass<BaseClass>{} 

AI回答一

image.png

这两个类是C#中的泛型类,其意义如下:

BaseClass<T> where T : BaseClass<T>, new(): 这个类定义了一个泛型类型T,它必须是BaseClass类型或其派生类型,并且必须有一个无参构造函数。也就是说,只能使用继承自BaseClass的类型作为类型参数T,这个限制也被称为递归泛型约束。该类可以用来定义一些需要 T 类型作为参数的方法、属性或字段。

public class BaseClass:BaseClass<BaseClass>{}: 这个类继承自BaseClass,即将BaseClass作为类型参数传入BaseClass,使得T被指定为BaseClass。这个类实际上是一个不带类型参数的BaseClass的特化版本,它的作用和BaseClass是一样的,只不过T在这里已经具体指定为BaseClass了。

总结一下,BaseClass<T> where T : BaseClass<T>, new()是定义泛型类的模板,而public class BaseClass:BaseClass<BaseClass>{}是使用该模板生成的一个具体类,它是一个不带类型参数的、继承自BaseClass的特化版本。

image.png

首先看 BaseClass<T> where T : BaseClass<T>, new() 这个类。这里定义了一个泛型类型 T,它有两个约束条件:必须是 BaseClass<T>BaseClass<T> 的子类,并且必须有一个无参构造函数。这里的 <T> 表示这是一个泛型类,可以用不同的类型参数来实例化该类,例如 BaseClass<Derived>,其中 DerivedBaseClass<T> 的派生类。

这个类的意义在于,我们希望定义一些方法、属性或字段,需要使用 T 类型作为参数或返回值,由于我们并不知道 T 具体是什么类型,所以我们需要通过泛型方式来定义这些方法、属性或字段。但是,如果我们使用 where T : class 这样的泛型约束,只能限制 T 必须是引用类型。而通过 where T : BaseClass<T>, new() 这样的递归泛型约束,我们可以限制 T 必须是 BaseClass<T> 类型或其派生类型,这样就更加精确地限定了 T 的类型范围。

接下来看 public class BaseClass:BaseClass<BaseClass>{} 这个类。这个类是 BaseClass<BaseClass> 的一个特化版本,它没有任何的类型参数,并且继承自 BaseClass<BaseClass>。由于 BaseClass<BaseClass> 满足泛型约束 where T : BaseClass<T>, new(),所以 public class BaseClass:BaseClass<BaseClass>{} 也满足这个泛型约束。

这个类的意义在于,它是 BaseClass<T> 的一个具体实现,其中的 T 被指定为 BaseClass。这样定义的好处在于,我们可以使用 BaseClass 类型来引用这个类,并且可以重载该类中定义的方法、属性或字段,从而扩展其功能。

希望这个解释可以让你更好地理解这两个类的含义和作用。

image.png

image.png

image.png

image.png

image.png

AI 回答二

image.png

这两个代码片段展示了c#中的泛型和继承。其中,public class baseclass<t> where t : baseclass<t>, new() 定义了一个泛型类,其限制条件要求泛型类型 t 必须是派生自 baseclass<t> 自身且能够默认构造。

public class baseclass:baseclass<baseclass>{} 继承自 baseclass<baseclass>,表示它是 baseclass<t> 泛型类在泛型实参为 baseclass 时的特化版本。由于 baseclass<baseclass> 满足 baseclass<t> 参数类型的限制条件,因此该语句是合法的。

这种用法对于一些框架库的设计非常有用,因为它让具体的子类能够获取到父类的类型信息,从而使得一些高度抽象的算法能够根据不同的子类类型来进行差异化的处理,实现更加灵活可扩展的代码设计。

当一个泛型类定义了一个泛型类型参数 T,如:public class BaseClass<T> where T : BaseClass<T>, new(),它限定了 T 类型必须是 BaseClass<T> 自身或其派生类,并且需要有默认构造函数。

这个泛型类的设计目的可能是为了让子类继承自 BaseClass<T> 后实现某些特定的行为。但是,在一些情况下,如果仅使用 BaseClass<T> 定义出来的泛型类,派生类并不能够获取到自身的类型信息。为了解决这个问题,我们可以对泛型类进行如下的特化:public class BaseClass : BaseClass<BaseClass>{},即使用 BaseClass 作为 BaseClass<T> 泛型类的实参,这样子类就可以通过继承 BaseClass 来获取自己的类型。

举个例子,假设我们需要设计一个数字计算框架,其中加法和乘法操作需要在具体的数字类型上执行。为了避免复制粘贴代码实现各种数字类型的加法和乘法,我们可以定义一个泛型类 NumericOperations<T> where T: INumeric,其中 INumeric 是一个接口类型,表示数字类型。在 NumericOperations<T> 类中,我们提供了一组抽象方法来实现加法和乘法操作。

此时,如果我们需要针对 int 类型来进行加法和乘法操作,可以这样定义一个具体的算子类:

    public class IntOperations : NumericOperations<int> { public override int Add(int a, int b) { return a + b; } public override int Multiply(int a, int b) { return a * b; } }

但是,如果我们要支持其他的数字类型,如 floatdouble,就需要再定义两个新的算子类来实现

动手测试

定义代码

      public abstract class Shape
    {
        public abstract double GetArea();
    }

    public class Rectangle : Shape
    {
        public double Width { get; set; }
        public double Height { get; set; }

        public override double GetArea()
        {
            return Width * Height;
        }
    }

    public class Circle : Shape
    {
        public double Radius { get; set; }

        public override double GetArea()
        {
            return Math.PI * Radius * Radius;
        }
    }

    public class Calculator<T> where T : Shape //相当于一个模板,包装盒
    {
        public double CalculateArea(T shape)
        {
            return shape.GetArea();
        }
    }

测试代码

    SonClass son=new SonClass();
            son.Address = "127.0.0.1";
            son.Age = 1;
            son.Test();
            Console.WriteLine($"年龄{son.Age} ");
            Console.WriteLine($"地址{son.Address} ");
            Console.WriteLine("================分割线1================");
            BaseClass baseT = new BaseClass();
            baseT.Test();
  
            baseT.Name = "Test";
            baseT.Age = 1;
            baseT.Id = 1000;
            baseT.Description = "我是非泛型类的属性";
            baseT.RunTest();
            Console.WriteLine("================分割线=========分割线================");
            BaseClass<SonClass> t=new BaseClass<SonClass>();
            t.Age = 100;
            t.Test();
            Console.WriteLine(t.Age);
           // BaseClass<Person> t2 = new BaseClass<Person>(); 这个不行
            Console.WriteLine("================分割线=========分割线================");

            BaseClass<SonClass>  myTest =new SonClass();
            myTest.Age= 99;
            myTest.Test(); // 调用的是重写方法,不是基类方法
            Console.WriteLine(myTest.Age);
            Console.WriteLine("================分割线=========分割线================");
            var calculator = new Calculator<Rectangle>();
            var rectangle = new Rectangle { Width = 3, Height = 4 };
            var area = calculator.CalculateArea(rectangle);
            Console.WriteLine($"Rectangle area: {area}");

            Calculator<Circle> calculator2 = new Calculator<Circle>(); //其实还是Calculator<Circle>
            var circle = new Circle { Radius = 2 };
            var area2 = calculator2.CalculateArea(circle);
            Console.WriteLine($"Circle area: {area2}");

            Console.WriteLine("================分割线=========分割线================");
            Person a1=new Person();
            a1.FirstName = "张";
            a1.LastName = "Test";
            Person a2 = new Person();
            a2.FirstName= "王";
            a2.LastName = "Test刚";
            Pair<string, string> pair = new Pair<string, string>("a","b");
            Console.WriteLine(pair.First);
            Console.WriteLine(pair.Second);
            Pair<string, string> pair2 = new Pair<string, string>("a", "b",100);
            Console.WriteLine("================分割线1================");
     
            Console.WriteLine(pair2.First);
            Console.WriteLine(pair2.Second);
            Console.WriteLine(pair2.Thrit);
            Pair<Person, Person> pair3 =new  Pair<Person, Person>(a1,a2);
            Console.WriteLine("================分割线2================");
            Console.WriteLine(pair3.First.FullName);
            Console.WriteLine(pair3.Second.FullName);

            Pair<Person, Person> pair4 = new Pair<Person, Person>(a1, a2,500);
            Console.WriteLine("================分割线3================");
            Console.WriteLine(pair4.First.FullName);
            Console.WriteLine(pair4.Second.FullName);
            Console.WriteLine(pair4.Thrit);

运行结果图

image.png