搜索

C#泛型编程

Dec 21, 2016 pm 02:47 PM

泛型:通过参数化类型来实现在同一份代码上操作多种数据类型。利用“参数化类型”将类型抽象化,从而实现灵活的复用。

例子代码:

class PRogram

    {

        static void Main(string[] args)

        {

            int obj = 2;

            Test test = new Test(obj);

            Console.WriteLine("int:" + test.obj);

            string obj2 = "hello world";

            Test test1 = new Test(obj2);

            Console.WriteLine("String:" + test1.obj);

            Console.Read();

        }

    }



    class Test

    {

        public T obj;

        public Test(T obj)

        {

            this.obj = obj;

        }

}

    输出结果是:

    int:2

String:hello world



程序分析:

1、  Test是一个泛型类。T是要实例化的范型类型。如果T被实例化为int型,那么成员变量obj就是int型的,如果T被实例化为string型,那么obj就是string类型的。

2、  根据不同的类型,上面的程序显示出不同的值。



C#泛型机制:

C#泛型能力有CLR在运行时支持:C#泛型代码在编译为IL代码和元数据时,采用特殊的占位符来表示范型类型,并用专有的IL指令支持泛型操作。而真正的泛型实例化工作以“on-demand”的方式,发生在JIT编译时。



看看刚才的代码中Main函数的元数据

.method private hidebysig static void  Main(string[] args) cil managed

{

  .entrypoint

  // Code size       79 (0x4f)

  .maxstack  2

  .locals init ([0] int32 obj,

           [1] class CSharpStudy1.Test`1 test,

           [2] string obj2,

           [3] class CSharpStudy1.Test`1 test1)

  IL_0000:  nop

  IL_0001:  ldc.i4.2

  IL_0002:  stloc.0

  IL_0003:  ldloc.0

  IL_0004:  newobj     instance void class CSharpStudy1.Test`1::.ctor(!0)

  IL_0009:  stloc.1

  IL_000a:  ldstr      "int:"

  IL_000f:  ldloc.1

  IL_0010:  ldfld      !0 class CSharpStudy1.Test`1::obj

  IL_0015:  box        [mscorlib]System.Int32

  IL_001a:  call       string [mscorlib]System.String::Concat(object,

                                                              object)

  IL_001f:  call       void [mscorlib]System.Console::WriteLine(string)

  IL_0024:  nop

  IL_0025:  ldstr      "hello world"

  IL_002a:  stloc.2

  IL_002b:  ldloc.2

  IL_002c:  newobj     instance void class CSharpStudy1.Test`1::.ctor(!0)

  IL_0031:  stloc.3

  IL_0032:  ldstr      "String:"

  IL_0037:  ldloc.3

  IL_0038:  ldfld      !0 class CSharpStudy1.Test`1::obj

  IL_003d:  call       string [mscorlib]System.String::Concat(string,

                                                              string)

  IL_0042:  call       void [mscorlib]System.Console::WriteLine(string)

  IL_0047:  nop

  IL_0048:  call       int32 [mscorlib]System.Console::Read()

  IL_004d:  pop

  IL_004e:  ret

} // end of method Program::Main



    再来看看Test类中构造函数的元数据

.method public hidebysig specialname rtspecialname 

        instance void  .ctor(!T obj) cil managed

{

  // Code size       17 (0x11)

  .maxstack  8

  IL_0000:  ldarg.0

  IL_0001:  call       instance void [mscorlib]System.Object::.ctor()

  IL_0006:  nop

  IL_0007:  nop

  IL_0008:  ldarg.0

  IL_0009:  ldarg.1

  IL_000a:  stfld      !0 class ConsoleCSharpTest1.Test`1::obj

  IL_000f:  nop

  IL_0010:  ret

} // end of method Test`1::.ctor



1、第一轮编译时,编译器只为Test类型产生“泛型版”的IL代码与元数据——并不进行泛型的实例化,T在中间只充当占位符。例如:Test类型元数据中显示的

2、JIT编译时,当JIT编译器第一次遇到Test时,将用int替换“范型版”IL代码与元数据中的T——进行泛型类型的实例化。例如:Main函数中显示的

3、CLR为所有类型参数为“引用类型”的泛型类型产生同一份代码;但是如果类型参数为“值类型”,对每一个不同的“值类型”,CLR将为其产生一份独立的代码。因为实例化一个引用类型的泛型,它在内存中分配的大小是一样的,但是当实例化一个值类型的时候,在内存中分配的大小是不一样的。



C#泛型特点:

1、如果实例化泛型类型的参数相同,那么JIT编辑器会重复使用该类型,因此C#的动态泛型能力避免了C++静态模板可能导致的代码膨胀的问题。

2、C#泛型类型携带有丰富的元数据,因此C#的泛型类型可以应用于强大的反射技术。

3、C#的泛型采用“基类、接口、构造器,值类型/引用类型”的约束方式来实现对类型参数的“显示约束”,提高了类型安全的同时,也丧失了C++模板基于“签名”的隐式约束所具有的高灵活性



C#泛型继承:

C#除了可以单独声明泛型类型(包括类与结构)外,也可以在基类中包含泛型类型的声明。但基类如果是泛型类,它的类型要么以实例化,要么来源于子类(同样是泛型类型)声明的类型参数,看如下类型

class C

class D:C

class E:C

class F:C

class G:C  //非法

E类型为C类型提供了U、V,也就是上面说的来源于子类

F类型继承于C,个人认为可以看成F继承一个非泛型的类

G类型为非法的,因为G类型不是泛型,C是泛型,G无法给C提供泛型的实例化



泛型类型的成员:

泛型类型的成员可以使用泛型类型声明中的类型参数。但类型参数如果没有任何约束,则只能在该类型上使用从System.Object继承的公有成员。如下图:




泛型接口:

泛型接口的类型参数要么已实例化,要么来源于实现类声明的类型参数



泛型委托:

泛型委托支持在委托返回值和参数上应用参数类型,这些参数类型同样可以附带合法的约束

delegate bool MyDelegate(T value);

class MyClass

{

    static bool F(int i){...}

    static bool G(string s){...}

    static void Main()

    {

        MyDelegate p2 = G;

        MyDelegate p1 = new MyDelegate(F);

    }

}



泛型方法:

1、C#泛型机制只支持“在方法声明上包含类型参数”——即泛型方法。

2、C#泛型机制不支持在除方法外的其他成员(包括属性、事件、索引器、构造器、析构器)的声明上包含类型参数,但这些成员本身可以包含在泛型类型中,并使用泛型类型的类型参数。

3、泛型方法既可以包含在泛型类型中,也可以包含在非泛型类型中。



泛型方法声明:如下

public static int FunctionName(T value){...}



泛型方法的重载:

public void Function1(T a);

public void Function1(U a);

这样是不能构成泛型方法的重载。因为编译器无法确定泛型类型T和U是否不同,也就无法确定这两个方法是否不同



public void Function1(int x);

public void Function1(int x);

这样可以构成重载



public void Function1(T t) where T:A;

public void Function1(T t) where T:B;

这样不能构成泛型方法的重载。因为编译器无法确定约束条件中的A和B是否不同,也就无法确定这两个方法是否不同



泛型方法重写:

在重写的过程中,抽象类中的抽象方法的约束是被默认继承的。如下:

abstract class Base

{

    public abstract T F(T t,U u) where U:T;

    public abstract T G(T t) where T:IComparable;

}



class MyClass:Base

{

    public override X F(X x,Y y){...}

    public override T G(T t) where T:IComparable{}

}

对于MyClass中两个重写的方法来说

F方法是合法的,约束被默认继承

G方法是非法的,指定任何约束都是多余的



泛型约束:

1、C#泛型要求对“所有泛型类型或泛型方法的类型参数”的任何假定,都要基于“显式的约束”,以维护C#所要求的类型安全。

2、“显式约束”由where子句表达,可以指定“基类约束”,“接口约束”,“构造器约束”,“值类型/引用类型约束”共四种约束。

3、“显式约束”并非必须,如果没有指定“显式约束”,范型类型参数将只能访问System.Object类型中的公有方法。例如:在开始的例子中,定义的那个obj成员变量。比如我们在开始的那个例子中加入一个Test1类,在它当中定义两个公共方法Func1、Func2,如下图:






下面就开始分析这些约束:

基类约束:

class A

    {

        public void Func1()

        { }

    }



    class B

    {

        public void Func2()

        { }

    }



    class C

        where S : A

        where T : B

    {

        public C(S s,T t)

        {

            //S的变量可以调用Func1方法

            s.Func1();

            //T的变量可以调用Func2方法

            t.Func2();

        }

    }

接口约束:

interface IA

    {

        T Func1();

    }



    interface IB

    {

        void Func2();

    }



    interface IC

    {

        T Func3();

    }



    class MyClass

        where T : IA

        where V : IB, IC

    {

        public MyClass(T t,V v)

        {

            //T的对象可以调用Func1

            t.Func1();

            //V的对象可以调用Func2和Func3

            v.Func2();

            v.Func3();

        }

    }

构造器约束:

class A

        {

            public A()

            { }

        }



        class B

        {

            public B(int i)

            { }

        }



        class C where T : new()

        {

            T t;

            public C()

            {

                t = new T();

            }

        }



        class D

        {

            public void Func()

            {

                C c = new C();

                C d = new C();

            }

        }

    d对象在编译时报错:The type B must have a public parameterless constructor in order to use it as parameter 'T' in the generic type or method C

    注意:C#现在只支持无参的构造器约束

    此时由于我们为B类型写入了一个有参构造器,使得系统不会再为B自动创建一个无参的构造器,但是如果我们将B类型中加一个无参构造器,那么对象d的实例化就不会报错了。B类型定义如下:

        class B

        {

            public B()

            { }

            public B(int i)

            { }

        }

值类型/引用类型:

public struct A { }

        public class B { }



        public class C where T : struct

        {



        }



        C
c1 = new C();

        C c2 = new C();

    c2对象在编译时报错:The type 'B' must be a non-nullable value type in order to use it as parameter 'T' in the generic type or methor 'C'

    

总结:

1、C#的泛型能力由CLR在运行时支持,它既不同于C++在编译时所支持的静态模板,也不同于java在编译器层面使用“擦拭法”支持的简单的泛型。

2、C#的泛型支持包括类、结构、接口、委托四种泛型类型,以及方法成员。

3、C#的泛型采用“基类,接口,构造器,值类型/引用类型”的约束方式来实现对类型参数的“显式约束”,它不支持C++模板那样的基于签名的隐式约束。

 以上就是C#泛型编程的内容,更多相关内容请关注PHP中文网(www.php.cn)! 


使用C#.NET开发:实用指南和示例使用C#.NET开发:实用指南和示例May 12, 2025 am 12:16 AM

C#和.NET提供了强大的功能和高效的开发环境。1)C#是一种现代、面向对象的编程语言,结合了C 的强大和Java的简洁性。2).NET框架是一个用于构建和运行应用程序的平台,支持多种编程语言。3)C#中的类和对象是面向对象编程的核心,类定义数据和行为,对象是类的实例。4).NET的垃圾回收机制自动管理内存,简化开发者的工作。5)C#和.NET提供了强大的文件操作功能,支持同步和异步编程。6)常见错误可以通过调试器、日志记录和异常处理来解决。7)性能优化和最佳实践包括使用StringBuild

C#.NET:了解Microsoft .NET框架C#.NET:了解Microsoft .NET框架May 11, 2025 am 12:17 AM

.NETFramework是一个跨语言、跨平台的开发平台,提供一致的编程模型和强大的运行时环境。1)它由CLR和FCL组成,CLR管理内存和线程,FCL提供预构建功能。2)使用示例包括读取文件和LINQ查询。3)常见错误涉及未处理异常和内存泄漏,需使用调试工具解决。4)性能优化可通过异步编程和缓存实现,保持代码可读性和可维护性是关键。

c#.net的寿命:其持久流行的原因c#.net的寿命:其持久流行的原因May 10, 2025 am 12:12 AM

C#.NET保持持久吸引力的原因包括其出色的性能、丰富的生态系统、强大的社区支持和跨平台开发能力。1)性能表现优异,适用于企业级应用和游戏开发;2).NET框架提供了广泛的类库和工具,支持多种开发领域;3)拥有活跃的开发者社区和丰富的学习资源;4).NETCore实现了跨平台开发,扩展了应用场景。

掌握C#.NET设计模式:从单胎到依赖注入掌握C#.NET设计模式:从单胎到依赖注入May 09, 2025 am 12:15 AM

C#.NET中的设计模式包括Singleton模式和依赖注入。1.Singleton模式确保类只有一个实例,适用于需要全局访问点的场景,但需注意线程安全和滥用问题。2.依赖注入通过注入依赖提高代码灵活性和可测试性,常用于构造函数注入,但需避免过度使用导致复杂度增加。

现代世界中的C#.NET:应用和行业现代世界中的C#.NET:应用和行业May 08, 2025 am 12:08 AM

C#.NET在现代世界中广泛应用于游戏开发、金融服务、物联网和云计算等领域。1)在游戏开发中,通过Unity引擎使用C#进行编程。2)金融服务领域,C#.NET用于开发高性能的交易系统和数据分析工具。3)物联网和云计算方面,C#.NET通过Azure服务提供支持,开发设备控制逻辑和数据处理。

C#.NET开发人员社区:资源和支持C#.NET开发人员社区:资源和支持May 06, 2025 am 12:11 AM

C#.NET开发者社区提供了丰富的资源和支持,包括:1.微软的官方文档,2.社区论坛如StackOverflow和Reddit,3.GitHub上的开源项目,这些资源帮助开发者从基础学习到高级应用,提升编程技能。

C#.NET优势:功能,好处和用例C#.NET优势:功能,好处和用例May 05, 2025 am 12:01 AM

C#.NET的优势包括:1)语言特性,如异步编程简化了开发;2)性能与可靠性,通过JIT编译和垃圾回收机制提升效率;3)跨平台支持,.NETCore扩展了应用场景;4)实际应用广泛,从Web到桌面和游戏开发都有出色表现。

See all articles

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

Video Face Swap

Video Face Swap

使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热门文章

热工具

SecLists

SecLists

SecLists是最终安全测试人员的伙伴。它是一个包含各种类型列表的集合,这些列表在安全评估过程中经常使用,都在一个地方。SecLists通过方便地提供安全测试人员可能需要的所有列表,帮助提高安全测试的效率和生产力。列表类型包括用户名、密码、URL、模糊测试有效载荷、敏感数据模式、Web shell等等。测试人员只需将此存储库拉到新的测试机上,他就可以访问到所需的每种类型的列表。

SublimeText3 英文版

SublimeText3 英文版

推荐:为Win版本,支持代码提示!

安全考试浏览器

安全考试浏览器

Safe Exam Browser是一个安全的浏览器环境,用于安全地进行在线考试。该软件将任何计算机变成一个安全的工作站。它控制对任何实用工具的访问,并防止学生使用未经授权的资源。

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具

Atom编辑器mac版下载

Atom编辑器mac版下载

最流行的的开源编辑器