在C#泛型中是否有合理的方法来处理“默认”类型参数?


91

在C ++模板中,可以指定某个类型参数为默认值。即,除非明确指定,否则它将使用类型T。

可以在C#中完成或近似吗?

我正在寻找类似的东西:

public class MyTemplate<T1, T2=string> {}

因此,该类型的实例未明确指定T2

MyTemplate<int> t = new MyTemplate<int>();

本质上将是:

MyTemplate<int, string> t = new MyTemplate<int, string>();

最终,我正在研究一个模板被广泛使用的情况,但是我正在考虑使用其他类型参数进行扩展。我想我可以继承,但是我很好奇是否还有其他选择。

Answers:


76

子类化是最好的选择。

我可以将您的主要通用类子类化:

class BaseGeneric<T,U>

有特定的班级

class MyGeneric<T> : BaseGeneric<T, string>

这样可以很容易地将逻辑保持在一个位置(基类),而且还可以轻松提供这两种用法选项。根据班级的不同,可能很少需要额外的工作来实现这一目标。


1
啊...有道理。如果类型参数提供唯一的签名,则类型名称是否可以相同?
el2iot2

2
@ee:是的,泛型overloadable按参数计数。
Mehrdad Afshari

@ee:是的,但是我会谨慎行事。这样做在.NET中是“合法的”,但可能导致混乱。我宁愿让字符串派生类型的名称与主要的通用类相似(因此很明显,很容易找到它),但是要使用一个使它明显是字符串的名称。
Reed Copsey

@里德:这真的会引起混乱吗?对于这种特定情况,我认为使用相同的名称甚至会有所帮助。.NET中有一些示例可以执行相同的操作:例如Func <>委托。
Mehrdad Afshari

4
例如,Predicate <T>只是Func <T,bool>,但由于其用途不同而被重命名。
Reed Copsey

19

一种解决方案是子类化。我要使用的另一个方法是工厂方法(与var关键字结合)。

public class MyTemplate<T1,T2>
{
     public MyTemplate(..args..) { ... } // constructor
}

public static class MyTemplate{

     public static MyTemplate<T1,T2> Create<T1,T2>(..args..)
     {
         return new MyTemplate<T1, T2>(... params ...);
     }

     public static MyTemplate<T1, string> Create<T1>(...args...)
     {
         return new MyTemplate<T1, string>(... params ...);
     }
}

var val1 = MyTemplate.Create<int,decimal>();
var val2 = MyTemplate.Create<int>();

在上面的示例中,val2是类型,MyTemplate<int,string> 而不是从其派生的类型。

类型class MyStringTemplate<T>:MyTemplate<T,string>与的类型不同MyTemplate<T,string>。在某些情况下,这可能会带来一些问题。例如,您不能将的实例转换MyTemplate<T,string>MyStringTemplate<T>


3
这是最有用的方法。非常好的解决方案
T-moty 2015年

12

您还可以像这样创建一个类Overload

public class MyTemplate<T1, T2> {
    public T1 Prop1 { get; set; }
    public T2 Prop2 { get; set; }
}

public class MyTemplate<T1> : MyTemplate<T1, string>{}

在发布较晚的答案之前,请阅读其他答案,因为您的解决方案可能与其他解决方案相同。
Cheng Chen

7
公认的答案是创建一个具有不同名称的类,我的解决方案是重载相同的类
Nerdroid 2015年

2
不,两者都在创建一个新类。名称在这里无关紧要。和两者都不MyTemplate<T1>是同一类。MyTemplate<T1, T2>AnotherTemplate<T1>
程晨

7
即使实类型不同(清晰且正确),如果您保留相同的名称,编码也会更容易。对于每个人来说,类“重载”是否可以使用这种方式并不明显。@DannyChen是正确的-从技术角度来看,结果是相同的,但此解决方案更接近实现OP的要求。
库巴

1
不幸的是,此解决方案仍然不如真正的“默认”泛型参数,因为MyTemplate<T1, string>无法将a分配给MyTemplate<T1>,这可能是
合乎


2

不幸的是,C#不支持您要执行的操作。鉴于参数的默认类型必须遵守通用约束,并且在CLR尝试确保类型安全时最有可能引起头痛,因此要实现该功能将非常困难。


1
并不是的。可以使用属性(例如VB.NET中的默认参数)完成此操作,并让编译器在编译时替换该属性。主要原因是C#设计目标。
Mehrdad Afshari

编译器必须确保默认参数满足通用约束。同样,默认参数本身就是通用约束,因为在方法中对类型参数所做的任何假设都将要求任何非默认类型参数都从其继承。
Andrew Hare

@Andrew,默认参数不必是通用约束。如果这样做的行为更像C ++中的默认模板参数,然后扩展到automatonic的类,则可以这样做:MyTemplate <int,float> x = null因为T2没有泛型约束,所以尽管使用默认字符串类型,float还是可以的。这样,默认模板参数本质上只是用于编写MyTemplate <int>作为MyTemplate <int,string>的简写的“语法糖”。
Tyler Laing

@Andrew,我同意C#编译器应验证此类默认模板参数是否满足现有的通用conststaints。C#编译器已经在类声明中验证了类似的东西,例如,向Reed的BaseGeneric <T,U>类添加诸如“ where U:ISomeInterface”之类的通用约束,然后MyGeneric <T>将无法编译并出现错误。验证默认模板参数几乎是一样的:编译器根据类的声明进行验证,并且错误消息可以相同或非常相似。
Tyler Laing

“这将很难实现”。实施起来很简单。根据模板约束验证类型的工作不会变得很困难,因为候选类型出现在文件中的其他位置(即,在模板中而不是在模板实例化时)。
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.