【发布时间】:2012-03-16 16:45:01
【问题描述】:
我想知道为什么对泛型类型参数的新约束只能在没有参数的情况下应用,也就是说,可以约束类型具有无参数构造函数,但不能约束类具有,例如,接收 int 作为参数的构造函数。我知道解决这个问题的方法,使用反射或工厂模式,可以正常工作。但我真的很想知道为什么,因为我一直在考虑它,我真的想不出无参数构造函数和带有参数的构造函数之间的区别,可以证明对新约束的这种限制是合理的。我错过了什么? 非常感谢
论据 1:构造函数是方法
@Eric:让我陪你一会儿:
构造函数是方法
那么我想如果我这样去没有人会反对:
public interface IReallyWonderful
{
new(int a);
string WonderMethod(int a);
}
但是一旦我有了它,我就会去:
public class MyClass<T>
where T : IReallyWonderful
{
public string MyMethod(int a, int b)
{
T myT = new T(a);
return myT.WonderMethod(b);
}
}
这是我最初想做的事情。所以,很抱歉,但不,构造函数不是方法,或者至少不完全是。
关于实施此功能的困难,我真的不知道,即使我知道,我也不会对明智地支出股东资金的决定发表任何意见。类似的东西,我会立即标记为答案。
从学术(我)的角度来看,也就是说,不考虑实施成本,问题确实是(我在过去几个小时内将其四舍五入):
应将构造函数视为类实现的一部分,还是应视为语义契约的一部分(就像将接口视为语义契约一样)。
如果我们将构造函数视为实现的一部分,那么约束泛型类型参数的构造函数并不是一件非常通用的事情,因为这会将您的泛型类型绑定到具体的实现,并且几乎可以说为什么要使用泛型?
作为实现的一部分的构造函数示例(将以下任何构造函数指定为ITransformer定义的语义契约的一部分是没有意义的):
public interface ITransformer
{
//Operates with a and returns the result;
int Transform(int a);
}
public class PlusOneTransformer : ITransformer
{
public int Transform(int a)
{
return a + 1;
}
}
public class MultiplyTransformer : ITransformer
{
private int multiplier;
public MultiplyTransformer(int multiplier)
{
this.multiplier = multiplier;
}
public int Transform(int a)
{
return a * multiplier;
}
}
public class CompoundTransformer : ITransformer
{
private ITransformer firstTransformer;
private ITransformer secondTransformer;
public CompoundTransformer(ITransformer first, ITransformer second)
{
this.firstTransformer = first;
this.secondTransformer = second;
}
public int Transform(int a)
{
return secondTransformer.Transform(firstTransformer.Transform(a));
}
}
问题在于构造函数也可能被视为语义契约的一部分,如下所示:
public interface ICollection<T> : IEnumerable<T>
{
new(IEnumerable<T> tees);
void Add(T tee);
...
}
这意味着,从一系列元素构建一个集合总是可行的,对吧?这将成为语义契约的一个非常有效的部分,对吧?
我在不考虑明智地使用股东资金的任何方面的情况下,倾向于允许构造函数作为语义合同的一部分。一些开发人员把它搞砸了,并限制某种类型具有一个语义不正确的构造函数,那么,同一个开发人员添加一个语义不正确的操作有什么区别?毕竟,语义契约就是这样,因为我们都同意它们是,而且因为我们都很好地记录了我们的库;)
论据 2:解析构造函数时的假设问题
@supercat 一直在尝试设置一些示例(引用评论)
如果不导致令人惊讶的行为,也很难准确定义构造函数约束应该如何工作。
但我真的不同意。在 C# 中(好吧,在 .NET 中)诸如“如何让企鹅飞起来?”之类的惊喜。根本不会发生。关于编译器如何解析方法调用有一些非常简单的规则,如果编译器无法解析它,那么它就不会通过,也不会编译。
他的最后一个例子是:
如果它们是逆变的,那么如果泛型类型具有约束 new(Cat, ToyotaTercel) 而实际类型只有构造函数 new(Animal, ToyotaTercel) 和 new (猫,汽车)。
好吧,让我们试试这个(我认为这与@supercat提出的情况类似)
class Program
{
static void Main(string[] args)
{
Cat cat = new Cat();
ToyotaTercel toyota = new ToyotaTercel();
FunnyMethod(cat, toyota);
}
public static void FunnyMethod(Animal animal, ToyotaTercel toyota)
{
Console.WriteLine("Takes an Animal and a ToyotaTercel");
}
public static void FunnyMethod(Cat cat, Automobile car)
{
Console.WriteLine("Takes a Cat and an Automobile");
}
}
public class Automobile
{ }
public class ToyotaTercel : Automobile
{ }
public class Animal
{ }
public class Cat : Animal
{ }
而且,哇,它不会因为错误而编译
以下方法或属性之间的调用不明确:“TestApp.Program.FunnyMethod(TestApp.Animal, TestApp.ToyotaTercel)”和“TestApp.Program.FunnyMethod(TestApp.Cat, TestApp.Automobile)”
如果相同的问题从具有参数化构造函数约束的解决方案中引起,我不明白为什么结果应该不同,如下所示:
class Program
{
static void Main(string[] args)
{
GenericClass<FunnyClass> gc = new GenericClass<FunnyClass>();
}
}
public class Automobile
{ }
public class ToyotaTercel : Automobile
{ }
public class Animal
{ }
public class Cat : Animal
{ }
public class FunnyClass
{
public FunnyClass(Animal animal, ToyotaTercel toyota)
{
}
public FunnyClass(Cat cat, Automobile car)
{
}
}
public class GenericClass<T>
where T: new(Cat, ToyotaTercel)
{ }
现在,当然,编译器无法处理构造函数上的约束,但如果可以,为什么错误不会出现在GenericClass<FunnyClass> gc = new GenericClass<FunnyClass>(); 行上,类似于尝试编译第一个示例时获得的错误,FunnyMethod 的那个。
无论如何,我会更进一步。当重写抽象方法或实现定义在接口上的方法时,需要使用完全相同的参数类型来执行此操作,不允许继承者或祖先。因此,当需要参数化构造函数时,应通过精确定义满足要求,而不是其他任何东西。在这种情况下,对于类GenericClass 的泛型参数类型,永远不能将类FunnyClass 指定为类型。
【问题讨论】:
-
Lippert:“我经常被问到为什么编译器不实现这个或那个特性,当然答案总是一样的:因为没有人实现它。特性开始时是未实现的只有当人们努力实现它们时才会被实现:不努力,没有特性。这当然是一个不令人满意的答案,因为通常提出问题的人已经假设该特性是如此明显地好,以至于我们需要有一个理由不实现它。实际上,无论多么明显的好,我们都不需要理由不实现任何功能。”
-
是的,没错,很明显。你是说这个功能不存在仅仅是因为没有人实现它吗?也就是说,实现它并不意味着在语言中引入 somo 不一致或矛盾,在这方面,无参数构造函数和其他构造函数之间确实没有区别。对?如果是这样,对我来说似乎有点随机,一直执行
new ()约束,然后将其留在那里。但话又说回来,如果你发明了 .NET,你就会按照你想要的方式来发明它,对吧? -
@PaulieWaulie:明智地花费股东的钱不是懒惰。
标签: c# generics constraints new-operator