Review后看到标题让我十分羞愧自己语文功底太差,估计...请见谅......我还特地把这句写回开头了......
前天遇到的一个问题,所以在MSDN发了个问,刚也丰富了下问题,关于泛型的。
最近用EF尝试DDD常常有些奇怪的想法,比如“EF的Model First能否添加泛型支持”。这次是“泛型的类型能否有带参数的约束方式”。
具体想法很简单,在我使用泛型的时候,我发现我需要实例化一个类型参数:
1 class MyClass<T> 2 { 3 public MyClass1() 4 { 5 this.MyObject = new T(); 6 } 7 8 T MyObject { get; set; } 9 }
当然,上面会报错。
错误内容是T没有一个new约束(new constraint),查阅下MSDN,得到了泛型的类型参数的new约束的内容。
所以接下来正确的代码就是:
1 class MyClass<T> 2 where T : new() 3 { 4 public MyClass1() 5 { 6 this.MyObject = new T(); 7 } 8 9 T MyObject { get; set; } 10 }
然后,后来我发现,我需要根据参数来创建新的对象,而且该方法在泛型的构造函数中实现最合适,所以我希望有这样的代码:
1 class MyClass1<T> 2 where T : new(string) 3 { 4 public MyClass(string request) 5 { 6 this.MyObject = new T(request); 7 } 8 9 T MyObject { get; set; } 10 }
可惜这下就错大了,然后查阅泛型的约束方式列表,发现根本没有带参数的构造函数这种约束。
所以就发生了我上面在MSDN上问的那个问题,寻求一个“优雅的解决方案”。
一般解决方案就像问题中的回答那样有两种,也是我试过但是很不爽的两种,我们依次看看。
首先是Factory Pattern,就是建一个工厂类,先看看代码,这是其中一种写法,请不要纠结在Factory Pattern上:
1 class MyClass<T, TFactory> 2 where TFactory : IFactory<T>, new() 3 { 4 public MyClass(string request) 5 { 6 var factory = new TFactory(); 7 8 this.MyObject = factory.New(request); 9 } 10 11 T MyObject { get; set; } 12 } 13 14 interface IFactory<T> 15 { 16 T New(string request); 17 }
实现中你会发现,这样需要为每个派生类或者实例类别创建并维护一个Factory类,那样泛型本身就没那么大意义了,本来就是为了减少类型重用逻辑而采用泛型的。
如果不想维护多一个类,那么就在目标类本身下手,所以我们可以为目标类创建一个基类:
1 class MyClass<T> 2 where T : TBase, new() 3 { 4 public MyClass(string request) 5 { 6 this.MyObject = T.New(request); 7 } 8 9 T MyObject { get; set; } 10 } 11 12 abstract class TBase 13 { 14 public abstract static TBase New(string request); 15 }
为了防止误人子弟,首先要说在前头的是,这样写是会编译错误的!
约束上是没错的,但是它报的错误是类似于“T是个类型参数,不能这么用!”('T' is a 'type parameter', which is not valid in the given context)。
至此,便可以知道,C#的泛型里,类型参数是一种“非类”的存在,类型参数的约束(Constraints on Type Parameters)仅仅是用来描述具体的类在实例化或者继承时所需要达到的条件。而在泛型内部,类型参数仅仅是一种“特别的存在”,它用来描述类,但却无法用作类。
首先,其实这个问题本身就是泛型的类型参数能否有带参数的实例化方式,比如 logs_code">T myObject = new T("Hello World!“) 。
然后,由于类型参数是用“约束”的方式来进行实例类的特点的描述的,所以,问题才变成了泛型的类型参数能否有带参数的构造函数的约束方式,比如 where T : new(string) 。
要做假设的话,起始就是个证伪的问题,要证明它存在是否会造成什么原则问题。
首先能对比的就是泛型的类型参数已经有了不带参数的构造函数的约束方式了,那么泛型的类型参数就算有带了参数的构造函数的约束方式又如何?至少,泛型的类型参数已经有了不带参数的构造函数的约束方式证明了泛型的类型参数有构造函数的约束方式并不会造成什么问题而且技术上是可以实现的。(......)
在我们实例化一个新对象的时候通常会用两种初始化方式:
大部分情况下两种方式产生的结果是差不多的,这种大部分情况是指一般所涉及到的属性或参数都是公开的(public),本来就是开放读写的,所以内部写和外部写都差不多。
但遇到一些情况,比如一些业务约束,需要对参数进行处理或者利用参数进行操作,最终操作结果是私密的(private),那么就会偏向于选用构造函数传参。或者会使用一个特殊的方法,由该方法在类实例化之后再把需要的数据带进来进行操作,这么做些许有失“一气呵成”的爽快。
利用构造函数传参并不是什么容易替代的方式,因为它在绝大部分属于它的场景里都是最优的解决方案。有时候,初始化一个对象到使用,一气呵成是最好的,因为这个事务本身就有很强的原子性。一个对象的两种初始化方式造成了双入口的麻烦,作为该类的使用者,有时候你会模糊,两种方式所产生的结果你无法准确地把握;对于开发者,两种实现方式供的出现在规范上也要求要么二选一,要么保证两者一致。当类变得相对复杂的时候,事情就没那么简单了。
所以,我们确实会需要泛型的类型参数有带了参数的构造函数的约束方式的一些场景。它虽然不是必要的,但是绝对是一种需要,就像get/set访问器那样。
如果你有不满,可以提供合适的标题,禁止以任何方式攻击作者!