简体   繁体   English

限制泛型类型参数以具有特定构造函数

[英]Restricting a generic type parameters to have a specific constructor

I'd like to know why the new constraint on a generic type parameter can only be applied without parameters, that is, one may constraint the type to have the parameterless constructor, but one cannot constraint the class to have, say, a constructor that receives a int as a parameter. 我想知道为什么泛型类型参数的新约束只能在没有参数的情况下应用,也就是说,可以将类型约束为具有无参数构造函数,但是不能将类约束为具有构造函数,接收一个int作为参数。 I know ways around this, using reflection or the factory pattern, that works fine, ok. 我知道这方面的方法,使用反射或工厂模式,工作正常,好吧。 But I'd really like to know why, because I've been thinking about it and I really can't think of a difference between a parameterless constructor and one with parameters that would justify this restriction on the new constraint. 但我真的很想知道为什么,因为我一直在思考它,我真的想不出无参数构造函数和带有参数的区别,这些参数可以证明对新约束的这种限制。 What am I missing? 我错过了什么? Thanks a lot 非常感谢


Argument 1: Constructors are methods 论点1:构造函数是方法

@Eric: Let me go here with you for a sec: @Eric:让我和你在一起一会儿:

Constructors are methods 构造函数是方法

Then I suppose no one would object if I'd go like this: 那么我想如果我这样做,没有人会反对:

public interface IReallyWonderful
{
    new(int a);

    string WonderMethod(int a);
}

But once I have that, then I'd go: 但是一旦我有了,那我就去:

public class MyClass<T>
        where T : IReallyWonderful
{
    public string MyMethod(int a, int b)
    {
        T myT = new T(a);
        return myT.WonderMethod(b);
    }
}

Which is what I wanted to do in the first place. 这首先是我想要做的。 So, sorry, but no, constructors are not methods , or at least not exactly. 所以,抱歉,但不, 构造函数不是方法 ,或者至少不完全。

On the difficulties of implementing this feature, well I'd really wouldn't know, and even if I did, I wouldn't have anything to say on a decision regarding the wisely expenditure of shareholder money. 关于实现这个功能的困难,我真的不知道,即使我这样做了,我对于明智地支出股东资金的决定也没什么可说的。 Something like that, I would've marked as an answer right away. 这样的话,我会马上把它标记为答案。

From an academic (my) point of view, and that is, without any regards for implementation costs, the question really is (I've rounded it up to this in the last few hours): 从学术(我)的角度来看,也就是说,没有任何关于实施成本的问题,问题确实是(我在最近几个小时内完成了这个问题):

Should constructors be considered as part of the implementation of a class, or as part of the semantic contract (in the same way an interface is considered a semantic contract). 构造函数应该被视为类的实现的一部分,还是作为语义契约的一部分(以同样的方式将接口视为语义契约)。

If we consider constructors as part of the implementation, then, constraining the constructor of a generic type parameter is not a very generic thing to do, since that'd be tying up your generic type to a concrete implementation, and one almost could say why use generics at all? 如果我们将构造函数视为实现的一部分,那么,约束泛型类型参数的构造函数不是一件非常通用的事情,因为这会将你的泛型类型绑定到具体的实现,并且几乎可以说为什么完全使用泛型?

Example of constructor as part of the implementation (no sense in specifying any of the following constructors as part of the semantic contract defined by ITransformer ): 构造函数的示例作为实现的一部分(在将指定任何以下构造函数作为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));
    }
}

The problem is that constructors may also be considered as part of the semantic contract, like so: 问题是构造函数也可能被视为语义契约的一部分,如下所示:

public interface ICollection<T> : IEnumerable<T>
{
    new(IEnumerable<T> tees);

    void Add(T tee);

    ...
}

This means, it's always posible to build a collection from a sequence of elements, right? 这意味着,从一系列元素构建集合总是可行的,对吧? And that would make a very valid portion of a semantic contract, right? 这将成为语义合同的一个非常有效的部分,对吗?

Me, without taking into account any of the aspects regarding the wisely expenditure of shareholder money, would favour allowing constructors as parts of semantic contracts. 在不考虑股东资金明智支出的任何方面的情况下,我倾向于允许构造函数作为语义契约的一部分。 Some developer messes it up and constraints a certain type to having a semantically incorrect constructor, well, what's the difference there from the same developer adding a semantically incorrect operation? 有些开发人员搞砸了它并将某种类型限制为具有语义错误的构造函数,那么,同一个开发人员添加语义错误操作的区别是什么? After all, semantic contracts are that, because we all agreed they are, and because we all document our libraries really well ;) 毕竟,语义契约就是这样,因为我们都同意它们,因为我们都很好地记录了我们的库;)


Argument 2: Supposed problems when resolving constructors 参数2:解析构造函数时提出的问题

@supercat is been trying to set some examples as how (quote from a comment) @supercat一直试图设置一些例子(引用评论)

It would also be hard to define exactly how constructor constraints should work, without resulting in surprising behaviors. 也很难确切地定义构造函数约束应该如何工作,而不会导致令人惊讶的行为。

but I really must disagree. 但我真的必须不同意。 In C# (well, in .NET) surprises like "How to make a penguin fly?" 在C#(好吧,在.NET中)的惊喜,如“如何让企鹅飞?” simply don't happen. 根本不要发生。 There are pretty straightforward rules as to how the compiler resolves method calls, and if the compiler can't resolve it, well, it won't pass, won't compile that is. 关于编译器如何解析方法调用有很简单的规则,如果编译器无法解析它,那么它将无法通过,也不会编译。

His last example was: 他的最后一个例子是:

If they are contravariant, then one runs into trouble resolving which constructor should be called if a generic type has constraint new(Cat, ToyotaTercel), and the actual type just has constructors new(Animal, ToyotaTercel) and new(Cat, Automobile). 如果它们是逆变的,那么如果泛型类型具有约束new(Cat,ToyotaTercel),并且实际类型只具有新的构造函数(Animal,ToyotaTercel)和新的(Cat,Automobile),那么解决应该调用哪个构造函数会遇到麻烦。

Well, lets try this (which in my opinion is a similar situation to that proposed by @supercat) 好吧,让我们尝试一下(在我看来,这与@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
{ }

And, wow, it won't compile with the error 而且,哇,它不会编译错误

The call is ambiguous between the following methods or properties: 'TestApp.Program.FunnyMethod(TestApp.Animal, TestApp.ToyotaTercel)' and 'TestApp.Program.FunnyMethod(TestApp.Cat, TestApp.Automobile)' 以下方法或属性之间的调用不明确:'TestApp.Program.FunnyMethod(TestApp.Animal,TestApp.ToyotaTercel)'和'TestApp.Program.FunnyMethod(TestApp.Cat,TestApp.Automobile)'

I don't see why the result should be different if the same probleme arouse out of a solution with parameterized constructor constraints, like so: 我不明白为什么结果应该是不同的,如果同样的问题是由具有参数化构造函数约束的解决方案引起的,如下所示:

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)
{ }

Now, of course, the compiler can't handle the constraint on the constructor, but if it could, why could't the error be, on the line GenericClass<FunnyClass> gc = new GenericClass<FunnyClass>(); 现在,当然,编译器无法处理构造函数的约束,但是如果可以的话,为什么错误就行, GenericClass<FunnyClass> gc = new GenericClass<FunnyClass>(); similar to that obtained when trying to compile the first example, that of the FunnyMethod . 类似于尝试编译第一个示例时获得的, FunnyMethod

Anyway, I'd go one step further. 无论如何,我更进一步。 When one overrides an abstract method or implements a method defined on an interface, one is required to do so with exactly the same parameters type, no inheritors or ancestors allowed. 当一个覆盖抽象方法或实现在接口上定义的方法时,需要使用完全相同的参数类型,不允许继承者或祖先。 So, when a parameterized constructor is required, the requirement should be met with an exact definition, not with anything else. 因此,当需要参数化构造函数时,应该使用精确定义来满足需求,而不是其他任何需求。 In this case, the class FunnyClass could never be specified as the type, for the generic parameter type of class GenericClass . 在这种情况下,对于类GenericClass的泛型参数类型,类FunnyClass永远不能被指定为类型。

Kirk Woll's quote from me of course is all the justification that is required; Kirk Woll的引用当然是我需要的所有理由; we are not required to provide a justification for features not existing. 我们不需要为存在的功能提供理由。 Features have enormous costs. 功能有巨大的成本。

However, in this specific case I can certainly give you some reasons why I would push back on the feature if it came up in a design meeting as a possible feature for a future version of the language. 但是,在这个特定的情况下,我当然可以给你一些理由,如果它在设计会议中作为未来语言版本的一个可能功能出现,我将推迟使用该功能。

To start with: consider the more general feature. 首先:考虑更一般的功能。 Constructors are methods . 构造函数是方法 If you expect there to be a way to say "the type argument must have a constructor that takes an int" then why is it not also reasonable to say "the type argument must have a public method named Q that takes two integers and returns a string?" 如果你希望有一种方法可以说“类型参数必须有一个带有int的构造函数”那么为什么说“类型参数必须有一个名为Q的公共方法,它需要两个整数并返回一个串?”

string M<T>(T t) where T has string Q(int, int)
{
    return t.Q(123, 456);
}

Does that strike you as a very generic thing to do? 这对你来说是一个非常通用的事情吗? It seems counter to the idea of generics to have this sort of constraint. 这似乎与泛型具有这种约束的想法背道而驰。

If the feature is a bad idea for methods, then why is it a good idea for methods that happen to be constructors ? 如果该特性对于方法来说是一个坏主意,那么为什么对于恰好是构造函数的方法来说是一个主意呢?

Conversely, if it is a good idea for methods and constructors, then why stop there? 相反,如果方法和构造者是一个好主意,那么为什么要停在那里呢?

string M<T>(T t) where T has a field named x of type string
{
    return t.x;
}

I say that we should either do the whole feature or don't do it at all . 我说我们要么应该做整个功能,要么根本不做 If it is important to be able to restrict types to have particular constructors, then let's do the whole feature and restrict types on the basis of members in general and not just constructors . 如果能够将类型限制为具有特定构造函数是很重要的,那么让我们执行整个功能并基于成员而不仅仅是构造函数来限制类型。

That feature is of course a lot more expensive to design, implement, test, document and maintain. 当然,该功能的设计,实施,测试,文档和维护成本更高。

Second point: suppose we decided to implement the feature, either the "just constructors" version or the "any member" version. 第二点:假设我们决定实现该功能,“只是构造函数”版本或“任何成员”版本。 What code do we generate? 我们生成什么代码? The thing about generic codegen is that it has been carefully designed so that you can do the static analysis once and be done with it. 关于通用codegen的事情是它经过精心设计,以便您可以进行一次静态分析并完成它。 But there is no standard way to describe "call the constructor that takes an int" in IL. 但是没有标准的方法来描述IL中的“调用带有int的构造函数”。 We would have to either add a new concept to IL, or generate the code so that the generic constructor call used Reflection . 我们必须向IL添加新概念,或者生成代码,以便通用构造函数调用使用Reflection

The former is expensive; 前者很贵; changing a fundamental concept in IL is very costly. 改变IL中的基本概念是非常昂贵的。 The latter is (1) slow, (2) boxes the parameter, and (3) is code that you could have written yourself. 后者是(1)慢,(2)框参数,(3)是您自己编写的代码。 If you're going to use reflection to find a constructor and call it, then write the code that uses reflection to find a constructor and call it. 如果您要使用反射来查找构造函数并调用它,那么编写使用反射的代码来查找构造函数并调用它。 If this is the code gen strategy then the only benefit that the constraint confers is that the bug of passing a type argument that does not have a public ctor that takes an int is caught at compile time instead of runtime . 如果这是代码生成策略,那么约束赋予的唯一好处是传递一个没有带有int的公共ctor的类型参数的错误在编译时而不是运行时被捕获 You don't get any of the other benefits of generics, like avoiding reflection and boxing penalties. 你没有得到泛型的任何其他好处,比如避免反射和拳击惩罚。

Summary 摘要

This is an attempt to capture the current information and workarounds on this question and present it as an answer. 这是尝试捕获此问题的当前信息和解决方法,并将其作为答案。

I find Generics combined with Constraints one of the most powerful and elegant aspects of C# (coming from a C++ templates background). 我发现Generics与Constraints相结合是C#最强大和优雅的方面之一(来自C ++模板背景)。 where T : Foo is great as it introduces capability to T while still constraining it to Foo at compile time. where T : Foo很棒,因为它引入了T的能力,同时在编译时仍将它限制为Foo。 In many cases, it has made my implementation simpler. 在许多情况下,它使我的实现更简单。 At first, I was a bit concerned as using generic types in this way can cause generics to grow through the code, but I have allowed it to do so and the benefits have greatly outweighed any downsides. 起初,我有点担心,因为以这种方式使用泛型类型会导致泛型通过代码增长,但我已经允许它这样做,并且好处大大超过了任何缺点。 However, the abstraction always falls down when it comes to constructing a Generic type that takes a parameter. 但是,在构造带参数的Generic类型时,抽象总是会失败。

The Problem 问题

When constraining a generic class, you are able to indicate that the generic must have a parameterless constructor and then instantiate it: 在约束泛型类时,您可以指示泛型必须具有无参数构造函数,然后实例化它:

public class Foo<T>
   where T : new()
{
    public void SomeOperation()
    {
        T something = new T();
        ...
    }
}

The problem is that one is only ably to constrain for parameterless constructors. 问题是人们只能很好地约束无参数构造函数。 This means that one of the workarounds suggested below needs to be used for constructors that have parameters. 这意味着下面建议的解决方法之一需要用于具有参数的构造函数。 As described below, the workarounds have drawbacks ranging from requiring additional code to being very dangerous. 如下所述,解决方法具有从需要额外代码到非常危险的缺点。 Also, if I have a class that has a public parameterless constructor that is used by a generic method, but somewhere down the track that class is changed so that the constructor now has a parameter then I need to change the design of the template and surrounding code to use one of the workarounds rather than new(). 另外,如果我有一个具有泛型方法使用的公共无参数构造函数的类,但是在轨道的某个地方更改了类,以便构造函数现在有一个参数,那么我需要更改模板的设计和周围代码使用其中一个解决方法而不是new()。

This is something that Microsoft definitely knows about, see these links on Microsoft Connect just as a sample (not counting the confused Stack Overflow users asking the question) here here here here here here here . 这是值得肯定微软知道,请参阅Microsoft连接这些链接只是作为一个样本(不算糊涂堆栈溢出用户提出的问题) 这里 这里 这里 这里 这里 这里 这里

They are all closed as 'Won't Fix' or 'By Design'. 它们都被关闭为“不会修复”或“按设计”。 The sad thing about that is that the issue is then locked and it is no longer possible to vote them up. 令人遗憾的是,问题随后被锁定,不再可能将其投票。 However you can vote here for the constructor feature. 但是,您可以在此处投票以获取构造函数功能。

The Workarounds 解决方法

There are three main types of workarounds, none of which are ideal: - 有三种主要类型的解决方法,其中没有一种是理想的: -

  1. Use Factories . 使用工厂 This requires a whole lot of boilerplate code and overhead 这需要大量的样板代码和开销
  2. Use Activator.CreateInstance(typeof(T), arg0, arg1, arg2, ...) . 使用Activator.CreateInstance(typeof(T),arg0,arg1,arg2,...) This is my least favourite as type safety is lost. 这是我最不喜欢的,因为类型安全性丢失了。 What if down the track you add a parameter to the constructor of type T? 如果在轨道上向T类型的构造函数添加参数,该怎么办? You get a runtime exception. 您会收到运行时异常。
  3. Use the Function/action approach. 使用功能/操作方法。 and here . 在这里 This is my favourite as it retains type safety and requires less boilerplate code. 这是我的最爱,因为它保留了类型安全性并且需要更少的样板代码。 However, it is still not as simple as new T(a,b,c) and as a generic abstraction often spans many classes, the class that knows the type is often a few classes away from the class that needs to instantiate it so that func gets passed around resulting in unnecessary code. 但是,它仍然没有新T(a,b,c)那么简单,并且作为通用抽象通常跨越许多类,知道类型的类通常是远离需要实例化它的类的几个类,以便func被传递,导致不必要的代码。

The Explanations 解释

A standard response is provided on Microsoft Connect which is: Microsoft Connect提供了标准响应,它是:

"Thank you for your suggestion. Microsoft has received a number of suggestions on changes to the constraint semantics of generic types, as well as doing its own work in this area. However at this time Microsoft cannot give any undertaking that changes in this area will be part of a future product release. Your suggestion will be noted to help drive decisions in this area. In the meantime the code sample below..." “感谢您的建议。微软已收到一些关于更改泛型类型的约束语义的建议,以及在这方面做自己的工作。但是,此时微软不能承诺在这方面的变化将是成为未来产品发布的一部分。您的建议将被注意到有助于推动该领域的决策。与此同时,下面的代码示例......“

The workaround is actually not my recommended workaround of all the options as it is not type safe and results in a runtime exception if you ever happen to add another parameter to the constructor. 解决方法实际上不是我推荐的所有选项的解决方法,因为它不是类型安全的,如果您碰巧在构造函数中添加了另一个参数,则会导致运行时异常。

The best explanation I can find is offered by Eric Lippert in this very stack overflow post . 我能找到的最佳解释是Eric Lippert在这个堆栈溢出帖子中提供的 I am very appreciative of this answer, but I think that further discussion is required on this at a user level and then at the technical level (probably by people who know more than me about the internals). 我非常感谢这个答案,但我认为需要在用户层面进行进一步的讨论,然后在技术层面进行讨论(可能是那些比我更了解内部情况的人)。

I also recently spotted that there is a good and detail by Mads Torgersen in this link (see "Posted by Microsoft on 3/31/2009 at 3:29 PM"). 我最近也发现Mads Torgersen 在这个链接中有一个很好的细节(参见“微软于2009年3月31日下午3:29发布”)。

The problem is that constructors are different from other methods in that we can already constrain methods as much as we need to by way of derivation constraints (interface or base class). 问题是构造函数与其他方法的不同之处在于我们已经可以通过派生约束(接口或基类)来限制方法。 There may be some cases where method constraints are beneficial, I have never needed them, however I continuously hit the parameterless constructor limitation. 在某些情况下,方法约束是有益的,我从来不需要它们,但是我不断地遇到无参数构造函数限制。 Of course, a general (not constructor-only) solution would be ideal and Microsoft would need to decide on this themselves. 当然, 一般(非构造函数)解决方案是理想的,微软需要自己决定。

The Suggestions 建议

Regarding the debatable benefit vs difficulty of implementation, I can appreciate this, but would make the following points: - 关于可辩论的利益与实施的难度,我可以理解这一点,但会提出以下几点: -

  1. There is great value in catching bugs at compile time rather than runtime (type safety in this case). 在编译时捕获错误而不是运行时很有价值(在这种情况下类型安全)。
  2. There seem to be other options that are not so dire. 似乎还有其他选择并不那么可怕。 There have been a few suggestions on how this might be implemented. 关于如何实施这一点,已经提出了一些建议。 Notably, Jon Skeet proposed 'static interfaces' as a way to solve this and it appears that explicit member constraints already exist in the CLR, but not in C#, see the comments here and the discussion here . 值得注意的是,乔恩斯基特提出了“静态接口”的方式来解决这个问题,它似乎是明确的成员约束已经存在于CLR,但不是在C#中,看到的评论在这里和讨论在这里 Also, the comment by kvb in Eric Lippert's response about the arbitrary member constraints. 另外, kvb在Eric Lippert关于任意成员约束的回应中的评论。

The Status 现状

Not about to happen in any shape of form as far as I can tell. 就我所知,不会发生任何形式的形式。

If one wants to have a method with a generic type T whose instances can be created using a single int parameter, one should have the method accept, in addition to type T , either a Func<int, T> or else a suitably-defined interface, possibly using something like: 如果想要一个具有泛型类型T的方法,其实例可以使用单个int参数创建,那么除了类型T之外,还应该让方法接受Func<int, T>或者适当定义界面,可能使用类似的东西:

static class IFactoryProducing<ResultType>
{
    interface WithParam<PT1>
    {
        ResultType Create(PT1 p1);
    }
    interface WithParam<PT1,PT2>
    {
        ResultType Create(PT1 p1, PT2 p2);
    }
}

(the code would seem nicer if the outer static class could be declared as an interface, but IFactoryProducing<T>.WithParam<int> seems clearer than IFactory<int,T> (since the latter is ambiguous as to which type is the parameter and which is the result). (如果外部静态类可以声明为接口,代码看起来会更好,但IFactoryProducing<T>.WithParam<int>似乎比IFactory<int,T>更清晰(因为后者对参数的类型不明确)哪个是结果)。

In any case, whenever one passes aroud type T one also passes around a suitable factory delegate or interface, one can achieve 99% of what one could achieve with parameterized constructor constraints. 在任何情况下,每当一个传递aroud类型T也会传递一个合适的工厂委托或接口,可以实现99%的参数化构造函数约束。 The run-time cost can be minimized by having each constructable type generate a static instance of a factory, so it won't be necessary to create factory instances in any sort of looping context. 通过让每个可构造类型生成工厂的静态实例,可以最小化运行时成本,因此不需要在任何类型的循环上下文中创建工厂实例。

BTW, beyond the cost of the feature, there would almost certainly be some substantial limitations which would make it less versatile than the workaround. 顺便说一句,除了该功能的成本之外,几乎肯定存在一些实质性的限制,这将使其比解决方法更不通用。 If constructor constraints are not contravariant with regard to parameter types, it may be necessary to pass around a type parameter for the exact type required for the constructor constraint, in addition to the actual type of the parameter to be used; 如果构造函数约束在参数类型方面不是逆变的,那么除了要使用的参数的实际类型之外,可能还需要传递构造函数约束所需的确切类型的类型参数。 by the time one does that, one might as well pass around a factory. 当一个人做到这一点时,人们可能会绕过工厂。 If they are contravariant, then one runs into trouble resolving which constructor should be called if a generic type has constraint new(Cat, ToyotaTercel) , and the actual type just has constructors new(Animal, ToyotaTercel) and new(Cat, Automobile) . 如果它们是逆变的,那么如果泛型类型具有约束new(Cat, ToyotaTercel) ,并且实际类型只具有new(Animal, ToyotaTercel)构造函数new(Animal, ToyotaTercel)new(Cat, Automobile) ,那么解决应该调用哪个构造函数会遇到麻烦。

PS--To clarify the problem, contravariant constructor constraints lead to a variation of the "double diamond" problem. PS - 为了澄清问题,逆变构造函数约束导致“双钻石”问题的变化。 Consider: 考虑:

T CreateUsingAnimalAutomobile<T>() where T:IThing,new(Animal,Automobile)
{ ... }

T CreateUsingAnimalToyotaTercel<T>() where T:IThing,new(Animal,ToyotaTercel)
{ return CreateUsingAnimalAutomobile<T>(); }

T CreateUsingCatAutomobile<T>() where T:IThing,new(Cat,Automobile)
{ return CreateUsingAnimalAutomobile<T>(); }

IThing thing1=CreateUsingAnimalToyotaTercel<FunnyClass>(); // FunnyClass defined in question
IThing thing2=CreateUsingCatAutomobile<FunnyClass>(); // FunnyClass defined in question

In processing the call to CreateUsingAnimalToyotaTercel<FunnyClass>() , the "Animal,ToyotaTercel" constructor should satisfy the constraint for that method, and the generic type for that method should satisfy a constraint for CreateUsingAnimalAutomobile<T>() . 在处理对CreateUsingAnimalToyotaTercel<FunnyClass>()的调用时,“Animal,ToyotaTercel”构造函数应满足该方法的约束,并且该方法的泛型类型应满足CreateUsingAnimalAutomobile<T>()的约束。 In processing the call to CreateUsingCatAutomobile<FunnyClass>() , the "Cat,Automobile" constructor should satisfy the constraint for that method, and the generic type for that method should satisfy the constraint for CreateUsingAnimalAutomobile<T>() . 在处理对CreateUsingCatAutomobile<FunnyClass>()的调用时,“Cat,Automobile”构造函数应满足该方法的约束,并且该方法的泛型类型应满足CreateUsingAnimalAutomobile<T>()的约束。

The problem is that both calls will invoke a call to the same CreateUsingAnimalAutomobile<SillyClass>() method, and that method has no way of knowing which constructor should be invoked. 问题是两个调用都会调用同一个CreateUsingAnimalAutomobile<SillyClass>()方法,并且该方法无法知道应该调用哪个构造函数。 Contravariance-related ambiguities aren't unique to constructors, but in most cases they're resolved through compile-time binding. 与方差相关的歧义并非构造函数所独有,但在大多数情况下,它们是通过编译时绑定来解决的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM