Why C# don't accept constructor requirements with parameters on generics?
使用C泛型,您可以拥有这样的类:
1
| class Foo <T > where T :new() {} |
这意味着T类型应该有一个不带参数的构造函数。如果我们能有:
1 2 3 4 5 6 7 8
| class Foo <T > where T : new(string)
{
private T CreateItem ()
{
string s ="";
return new T (s );
}
} |
有什么理由微软没有将此功能添加到语言中吗?
- 我想说这和这个有很大的关系:stackoverflow.com/questions/619856/&hellip;
- "我们不需要提供不包括功能的原因。相反,功能必须在成本效益的基础上得到证明。"——摘自stackoverflow.com/questions/3270463/&hellip;
Is there any reason that Microsoft haven't added this feature to the language?
您所描述的特性是更一般特性的特定情况,"允许存在需要特定方法的约束"。例如,您可能会说:
1 2 3 4
| void M<T>(T t) where T has an accessible method with signature double Foo(int)
{
double x = t.Foo(123);
} |
我们在C中没有这个特性,因为特性必须通过成本效益分析来证明。从设计和实现的角度来看,这将是一个相当昂贵的功能——这一功能不仅会将需求驱动到C语言,而且还会驱动到每一种.NET语言。什么是令人信服的优势,证明该功能的合理性?
另外:假设我们设计了这个特性。如何有效实施?通用类型系统中的约束经过精心设计,这样抖动一旦生成有效的代码,就可以为每个引用类型共享这些代码。如何为任意方法模式匹配生成有效的代码?当方法的槽可以在编译时被知道时,这种高效的分派就非常简单;有了这个特性,我们将不再具有这种优势。
您想要的特性是相同的特性,只是方法的类型被限制为构造函数。
记住,泛型的目的是让您编写泛型类型的代码。如果您需要的约束比类型系统中可以捕获的约束更具体,那么您可能试图滥用泛型。
- 如果这很难,那么为什么要允许无参数构造函数。
- @Ariel:您的问题的答案很简单:这个小得多的特性的成本是由它所带来的好处来证明的。
- 此外,已经存在一个很好的解决方法:创建一个接口。当然还有更多的管道,但这不是黑客或其他狡猾的东西。并且有很多只包含1或2个方法的接口,以及许多实现6个或更多接口的类。
- @迈克尔:就是这样。对于一般的问题来说,一个接口工作得很好---除了控制器。不能将.ctor放入接口。
- 为了回答您关于如何有效地实现它的问题,一种方法(由F采取,它允许这样的约束)是在每个调用站点内联一个特定于类型的代码版本,从而失去了共享的好处。
- 现在,我们不再真正需要这个特性了——强制转换到dynamic允许您在不需要显式语言支持的情况下完成它。当然,如果语言和/或clr支持它,那么它的效率可能会比它低。
- 埃里克,我能从你那里正确地理解抖动与开放的仿制药的关系吗?但是怎么办?我的意思是这个短语"这样抖动可以生成一次有效的代码,然后可以为每个引用类型共享",为什么只有引用类型?
- @沃罗宁:如果你有一个方法M(),你调用M和M,那么抖动只生成一次机器代码。你明白为什么它能安全地这么做吗?所有引用的大小都相同,所有虚拟方法调用都位于同一插槽中。只有像x is T和其他类型测试这样的东西需要动态修复,但是这些很容易参数化。
- @埃里克。没想到。谢谢。我相信问题是"为什么某些功能没有实现?"应该理解为"我想知道团队以一种方式而不是另一种方式设计特性的原因是什么?"如果没有深入的知识和专业知识,很难猜测动机。
与其试图猜测微软为什么要决定一个特定的实现,不如使用工厂模式为您提供一个解决方案。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| public interface IFactory <T >
{
T CreateItem (string s );
}
class Foo <TFactory,T > where TFactory : IFactory <T >, new()
{
private T CreateItem ()
{
var factory = new TFactory ();
string s ="";
return factory .CreateItem(s );
}
} |
使用此模式,假设您有一个类Bar,它有一个采用单个字符串的构造函数:
1 2 3 4 5
| public class Bar
{
public Bar(string laa)
{}
} |
你只需要一个实现IFactory的BarFactory。
1 2 3 4 5 6 7 8
| public class BarFactory : IFactory <Bar >
{
public BarFactory () {}
public Bar CreateItem (string s )
{
return new Bar (s );
}
} |
现在你可以用那个工厂和foo
1
| var foo = new Foo <BarFactory,Bar >(); // calls to CreateItem() will construct a Bar |
- 工厂模式是一个很好的模式,这是一个很好的使用它,但这不是我的问题的答案。使用t:new()规范无法解决所有方案。当类具有多个参数时,它会变脏。通常我会这样做,最近我想知道为什么这不是语言的一部分?这就是问题所在。
- @很公平,但是a)这可能对其他人有帮助b)你的问题没有有效的答案。加上c)如果为IFactory选择了更具体的名称,则可以扩展为提供任何参数组合。
当我需要一个用参数调用构造函数的通用函数时,我采用的解决方案是使用反射来查找和调用它。
考虑到我同时控制了泛型和它被实现的所有类(而这个函数是构造这些类的唯一地方),我认为最好给类一个默认的构造函数,并在它们都实现的接口中添加一个初始化方法。
(允许将静态方法一般添加到接口的解决方案是理想的。)