C# 泛型约束中的派生约束使用 C# 泛型,编译器会将一般代码编译为 IL,而不管客户端将使用什么样的类型实参。因此,一般代码可以尝试使用与客户端使用的特定类型实参不兼容的一般类型参数的方法、属性或成员。这是不可接受的,因为它相当于缺少类型安全。在 C# 中,您需要通知编译器客户端指定的类型必须遵守哪些约束,以便使它们能够取代一般类型参数而得到使用。存在三个类型的约束。派生约束指示编译器一般类型参数派生自诸如接口或特定基类之类的基类型。默认构造函数约束指示编译器一般类型参数公开了默认的公共构造函数(不带任何参数的公共构造函数)。引用/值类型约束将一般类型参数约束为引用类型或值类型。一般类型可以利用多个约束,您甚至可以在使用一般类型参数时使 IntelliSense 反射这些约束,例如,建议基类型中的方法或成员。
C# 泛型约束中派生约束实例演示及使用方法:
在 C# 2.0 中,可以使用 where 保留关键字来定义约束。在一般类型参数中使用 where 关键字,后面跟一个派生冒号,以指示编译器该一般类型参数实现了特定接口。例如,以下为实现 LinkedList 的 Find() 方法所必需的派生约束:
- public class LinkedList where K : IComparable
- {
- T Find(K key)
- {
- Node current = m_Head;
- while(current.NextNode != null)
- {
- if(current.Key.CompareTo(key) == 0)
- break;
- else
- current = current.NextNode;
- }
- return current.Item;
- }
- //Rest of the implementation
- }
您还将在您约束的接口的方法上获得 IntelliSense 支持。
当客户端声明一个 LinkedList 类型的变量,以便为列表的键提供类型实参时,客户端编译器将坚持要求键类型派生自 IComparable,否则,将拒绝生成客户端代码。
请注意,即使该约束允许您使用 IComparable,它也不会在所使用的键是值类型(例如,整型)时,消除装箱所带来的性能损失。为了克服该问题,System.Collections.Generic 命名空间定义了一般接口 IComparable:
- public interface IComparable
- {
- int CompareTo(T other);
- bool Equals(T other);
- }
您可以约束键类型参数以支持 IComparable,并且使用键的类型作为类型参数;这样,您不仅获得了类型安全,而且消除了在值类型用作键时的装箱操作:
- public class LinkedList where K : IComparable
- {...}
实际上,所有支持 .NET 1.1 中的 IComparable 的类型都支持 .NET 2.0 中的 IComparable。这使得可以使用常见类型(例如,int、string、GUID、DateTime 等等)的键。
在 C# 2.0 中,所有约束都必须出现在一般类的实际派生列表之后。例如,如果 LinkedList 派生自 IEnumerable 接口(以获得迭代器支持),则需要将 where 关键字放在紧跟它后面的位置:
- public class LinkedList : IEnumerable where K : IComparable
- {...}
通常,只须在需要的级别定义约束。在链表示例中,在节点级别定义 IComparable 派生约束是没有意义的,因为节点本身不会比较键。如果您这样做,则您还必须将该约束放在 LinkedList 级别,即使该列表不比较键。这是因为该列表包含一个节点作为成员变量,从而导致编译器坚持要求:在列表级别定义的键类型必须遵守该节点在一般键类型上放置的约束。
换句话说,如果您按如下方式定义该节点:
- class Node where K : IComparable
- {...}
则您必须在列表级别重复该约束,即使您不提供 Find() 方法或其他任何与此有关的方法:
- public class LinkedList where KeyType : IComparable
- {
- Node﹤KeyType,DataType m_H﹥ead;
- }
您可以在同一个一般类型参数上约束多个接口(彼此用逗号分隔)。例如:
- public class LinkedList where K : IComparable,IConvertible
- {...}
您可以为您的类使用的每个一般类型参数提供约束,例如:
- public class LinkedList where K : IComparable
- where T : ICloneable
- {...}
您可以具有一个基类约束,这意味着规定一般类型参数派生自特定的基类:
- public class MyBaseClass
- {...}
- public class LinkedList where K : MyBaseClass
- {...}
但是,在一个约束中最多只能使用一个基类,这是因为 C# 不支持实现的多重继承。显然,您约束的基类不能是密封类或静态类,并且由编译器实施这一限制。此外,您不能将 System.Delegate 或 System.Array 约束为基类。
您可以同时约束一个基类以及一个或多个接口,但是该基类必须首先出现在派生约束列表中:
- public class LinkedList where K : MyBaseClass, IComparable
- {...}
C# 确实允许您将另一个一般类型参数指定为约束:
- public class MyClass where T : U
- {...}
在处理派生约束时,您可以通过使用基类型本身来满足该约束,而不必非要使用它的严格子类。例如:
- public interface IMyInterface
- {...}
- public class MyClass where T : IMyInterface
- {...}
- MyClass obj = new MyClass();
或者,您甚至可以:
- public class MyOtherClass
- {...}
- public class MyClass where T : MyOtherClass
- {...}
- MyClass obj = new MyClass();
C# 泛型约束中派生约束需要注意的:
在提供派生约束时,您约束的基类型(接口或基类)必须与您定义的一般类型参数具有一致的可见性。例如,以下约束是有效的,因为内部类型可以使用公共类型:
- public class MyBaseClass
- {}
- internal class MySubClass where T : MyBaseClass
- {}
但是,如果这两个类的可见性被颠倒,例如:
- internal class MyBaseClass
- {}
- public class MySubClass where T : MyBaseClass
- {}
则编译器会发出错误,因为程序集外部的任何客户端都无法使用一般类型 MySubClass,从而使得 MySubClass 实际上成为内部类型而不是公共类型。外部客户端无法使用 MySubClass 的原因是,要声明 MySubClass 类型的变量,它们需要使用派生自内部类型 MyBaseClass 的类型。
C# 泛型约束中的派生约束相关的内容就向你介绍到这里,希望对你了解和学习C# 泛型约束中的派生约束有所帮助。
【编辑推荐】