简体   繁体   English

通用类型约束:如何创建 C# 文档中描述的 class 的实例

[英]Generic type constraint: How do I create an instance of the class described in this C# documentation

where (generic type constraint) (C# Reference) where(泛型类型约束)(C# 参考)

The relevant text from the document cited above is:上面引用的文件中的相关文本是:

For example, you can declare a generic class, MyGenericClass, such that the type parameter T implements the IComparable<T> interface:例如,您可以声明一个泛型 class,MyGenericClass,这样类型参数 T 就实现了 IComparable<T> 接口:

Here is MyGenericClass , copied verbatim from documentation cited above:这是MyGenericClass ,从上面引用的文档中逐字复制:

public class AGenericClass<T> where T : IComparable<T> { }

Here is a small console app shows my attempts to create the instance:这是一个小型控制台应用程序,显示了我创建实例的尝试:

class Program
{
    static void Main(string[] args)
    {
        AGenericClass<MyComparable<string>> stringComparer = new AGenericClass<MyComparable<string>>(); // does not build


        AGenericClass<MyStringComparable> stringComparer2 = new AGenericClass<MyStringComparable>(); // does not build
    }
}


public class MyStringComparable : IComparable<string>
{
    public int CompareTo(string other) => throw new NotImplementedException();
}

public class MyComparable<T> : IComparable<T>
{
    public int CompareTo(T other) => throw new NotImplementedException();
}

// verbatim from documentation cited above
public class AGenericClass<T> where T : IComparable<T> { }  

For clarity my question is "How do I create an instance of AGenericClass as it is defined in the cited C# documentation".为了清楚起见,我的问题是“如何创建AGenericClass的实例,因为它在引用的 C# 文档中定义”。 Please note my question relates to the specific example cited above, not other related questions such as this one and this one .请注意,我的问题与上面引用的具体示例有关,而不是其他相关问题,例如 this one 和 this one

I'm obviously very confused on how type parameters work.我显然对类型参数的工作方式感到非常困惑。 I hope by answering this question I will become enlightened as it closely resembles the business problem I'm trying to solve.我希望通过回答这个问题,我会有所启发,因为它与我试图解决的业务问题非常相似。

Also my question has nothing to do with IComparable<T> or comparing objects - that just happens to be the interface in the example code.此外,我的问题与IComparable<T>或比较对象无关 - 这恰好是示例代码中的接口。

Edit: Additional code provided based on reply from @CoolBots.编辑:根据@CoolBots 的回复提供的附加代码。 This code provides a more realistic example and shows an interface that is intended to operate on a single object:此代码提供了一个更真实的示例,并显示了一个旨在在单个 object 上运行的接口:

public class Program2
{

    public Program2()
    {
        Selector<Selectable<string>, string> stringSelector = new Selector<Selectable<string>, string>();
    }
}

public interface ISelectable<T>
{
    bool IsSelected { get; set; }
    T Item { get; set; }
}

public class Selectable<T> : ISelectable<T>
{
    public bool IsSelected { get; set; }
    public T Item { get; set; }
}

public class Selector<T, TData> where T:ISelectable<TData>
{
}

In your specific example, the generic parameter T in AGenericClass<T> , and the corresponding constraint, where T: IComparable<T> specify that the generic parameter T must implement IComparable<T> interface (which is not important to your question, but important to understanding what's going on, since this interface itself is generic).在您的具体示例中, AGenericClass<T>中的泛型参数T和相应的约束, where T: IComparable<T>指定泛型参数T必须实现IComparable<T>接口(这对您的问题并不重要,但是理解发生了什么很重要,因为这个接口本身是通用的)。 Note that T in IComparable<T> is the same T as in AGenericClass<T> - that is, you're restricting to a type that implements IComparable<T> of itself - a string is a great example, because it is IComparable<string> However, your MyStringComparable is not an IComparable<MyStringComparable> - it is an IComparable<string> , which fails the generic type constraint on AGenericClass<T> , as defined.请注意, IComparable<T> TTAGenericClass<T>中的 T 相同 - 也就是说,您限制为实现自身IComparable<T>的类型 - string就是一个很好的例子,因为它是IComparable<string>但是,您的MyStringComparable不是IComparable<MyStringComparable> - 它是一个IComparable<string> ,它不符合定义的AGenericClass<T>的泛型类型约束。

The solution here is to use 2 generic parameters - one for container, and one for the type contained:这里的解决方案是使用 2 个泛型参数 - 一个用于容器,一个用于包含的类型:

class AGenericClass<TContainer, TData> where TContainer: IComparable<TData> { }

Instantiation is as follows:实例化如下:

var stringComparer = new AGenericClass<MyComparable<string>, string>();

var stringComparer2 = new AGenericClass<MyStringComparable, string>();

EDIT , based on your updated question and comments:编辑根据您更新的问题和评论:

If you really want to get rid of the second generic parameter, something's gotta give - for instance, we can lose the ability to specify a container, making it a pass-through , similar to System.Collections.Generic.LinkedList<T> 's implementation - LinkedListNode<T> is a pass-through container, which is known to the LinkedList<T> class, and cannot be swapped out for a different kind of container:如果您真的想摆脱第二个通用参数,则必须提供一些东西- 例如,我们可能会失去指定容器的能力,使其成为pass-through ,类似于System.Collections.Generic.LinkedList<T> ' s 实现 - LinkedListNode<T>是一个直通容器,它是LinkedList<T> class 已知的,不能换成不同类型的容器:

public class Selector<T> //no constraint at all
{
   private Selectable<T> container; //the container is always Selectable<T>

   public Selector(T item)
      => container = new Selectable<T>() { Item = item };
}

Usage is simpler:用法更简单:

var selector = new Selector<string>("some data"); // no need to pass Selectable<string> here

However, you are now stuck with the Selectable<T> as the container - you can't make a BetterSelectable<T> and use it as the container interchangeably.但是,您现在坚持将Selectable<T>作为容器 - 您不能制作BetterSelectable<T>并将其用作容器可互换。 Basically, it comes down to your use case - do you need 2 variables - a "container" and a "data contained", or are you ok with only one - just the data contained.基本上,这取决于您的用例 - 您是否需要 2 个变量- 一个“容器”和一个“包含的数据”,或者您只需要一个变量 - 只是包含的数据。 In the former case, you must have 2 generic parameters (because you have 2 variables);在前一种情况下,您必须有 2 个泛型参数(因为您有 2 个变量); in the latter case, you only need 1 generic parameter (because you have 1 variable, and one constant, namely the container).在后一种情况下,您只需要 1 个通用参数(因为您有 1 个变量和一个常量,即容器)。

For clarity my question is "How do I create an instance of AGenericClass as it is defined in the cited C# documentation"为了清楚起见,我的问题是“如何创建 AGenericClass 的实例,因为它在引用的 C# 文档中定义”

By providing a type for the type parameter T that fulfills the constraint T: IComparable<T> .通过为类型参数T提供满足约束T: IComparable<T> In your code example:在您的代码示例中:

static void Main(string[] args)
{
    AGenericClass<MyComparable<string>> stringComparer = new AGenericClass<MyComparable<string>>(); // does not build


    AGenericClass<MyStringComparable> stringComparer2 = new AGenericClass<MyStringComparable>(); // does not build
}

You get compile-time errors because neither of the types you've provided satisfy that constraint.您会收到编译时错误,因为您提供的任何类型都不满足该约束。 The way to fix the errors is to provide a type that does satisfy the constraint.修复错误的方法是提供一个满足约束的类型。

There are lots of types that already do satisfy the constraint.有很多类型已经满足了约束。 For example, int or any other primitive numeric type would.例如, int或任何其他原始数字类型都可以。 But presumably your question really is meant to be worded something more like this:但大概你的问题真的应该用更像这样的措辞:

For clarity my question is "How do I create an instance of AGenericClass as it is defined in the cited C# documentation, using a user-defined type of my own as the type parameter"为了清楚起见,我的问题是“如何使用我自己的用户定义类型作为类型参数来创建引用的 C# 文档中定义的 AGenericClass 的实例”

The answer is still the same: satisfy the constraint in your user-defined type.答案还是一样的:满足用户定义类型的约束。 So, why don't the types you already have do that?那么,为什么你已经拥有的类型不这样做呢? Let's see...让我们来看看...

Here are your types:以下是您的类型:

public class MyStringComparable : IComparable<string>
{
    public int CompareTo(string other) => throw new NotImplementedException();
}

public class MyComparable<T> : IComparable<T>
{
    public int CompareTo(T other) => throw new NotImplementedException();
}

Let's test them one at a time.让我们一次一个地测试它们。 Let's suppose we set T to MyStringComparable .假设我们将T设置为MyStringComparable To satisfy the constraint, that type would need to implement IComparable<T> where T is the type you've provided as the type parameter.为了满足约束,该类型需要实现IComparable<T>其中T是您作为类型参数提供的类型。 Since you set T to MyStringComparable , then MyStringComparable needs to implement IComparable<MyStringComparable> .由于您将T设置为MyStringComparable ,因此MyStringComparable需要实现IComparable<MyStringComparable>

Does it?可以? No, it does not.不,不是的。 It implements IComparable<T> for some other type parameter T , namely string .它为其他一些类型参数T实现IComparable<T> ,即string

You would need something like this, in order to satisfy the constraint:为了满足约束,您需要这样的东西:

public class MyStringComparable : IComparable<MyStringComparable>
{
    public int CompareTo(MyStringComparable other) => throw new NotImplementedException();
}

Okay, what about the second one.好吧,第二个呢。 Do the same test, using MyComparable<T> as the type parameter.使用MyComparable<T>作为类型参数进行相同的测试。 Of course, you need to provide a type parameter for that class, since it's generic.当然,您需要为该 class 提供类型参数,因为它是通用的。 In your example, you use string as the type parameter, so it's MyComparable<string> .在您的示例中,您使用string作为类型参数,因此它是MyComparable<string>

According to the constraint, the type parameter MyComparable<string> needs to implement IComparable<MyComparable<string>> .根据约束,类型参数MyComparable<string>需要实现IComparable<MyComparable<string>> Does it?可以? No, it does not.不,不是的。 The MyComparable<string> class only implements IComparable<string> . MyComparable<string> class 仅实现IComparable<string> Again, this is the wrong constraint.同样,这是错误的约束。

A class that would implement the correct constraint would look more like this:实现正确约束的 class 看起来更像这样:

public class MyComparable<T> : IComparable<MyComparable<T>>
{
    public int CompareTo(MyComparable<T> other) => throw new NotImplementedException();
}

Generic type constraints can be tricky to deal with.泛型类型约束可能很难处理。 Another area that people often get tangled up in is where trying to inherit or otherwise use an existing type parameter that has a constraint, forgetting that they need to satisfy the constraint in that context.人们经常纠结的另一个领域是尝试继承或以其他方式使用具有约束的现有类型参数,忘记了他们需要在该上下文中满足约束。 Often this is done by repeating the constraint in their own generic class or method.通常这是通过在他们自己的通用 class 或方法中重复约束来完成的。

I'll also note with respect to this text in your question:关于您的问题中的这段文字,我还会注意:

Also my question has nothing to do with IComparable or comparing objects - that just happens to be the interface in the example code.此外,我的问题与 IComparable 或比较对象无关 - 这恰好是示例代码中的接口。

Actually, your question has at least a moderate something to do with IComparable<T> , because it's often used in this sort of recursive way that trips people up.实际上,您的问题至少与IComparable<T>有一定程度的关系,因为它经常以这种递归方式使用,使人们绊倒。 You wouldn't have run into the same problem with a more conventional scenario.在更传统的情况下,您不会遇到同样的问题。

The scenario you've run into is the C# variation of C++'s "Curiously Recurring Template Pattern".您遇到的场景是 C++ 的“Curiously Recurring Template Pattern”的 C# 变体。 Eric Lippert's written a useful article on the topic , which you might like to read as you explore and learn about generic type parameters and their constraints. Eric Lippert 就该主题撰写了一篇有用的文章,您可能希望在探索和了解泛型类型参数及其约束时阅读该文章。

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

相关问题 如何创建通用类型类的新实例 - How do I create new instance of Generic Type Class 如何在c#中为泛型类型创建实例 - how to create instance for a generic type in c# C#Generic Constraint - 如何引用当前类类型? - C# Generic Constraint - How to refer to current class type? 如何将 `where T : U` 泛型类型参数约束从 C# 转换为 F#? - How do I translate a `where T : U` generic type parameter constraint from C# to F#? 如何约束通用类型参数以仅接受C#中的可为空的值类型? - How do I constraint a generic type parameter to only accept nullable value types in C#? 如何将接口用作 C# 泛型类型约束? - How can I use interface as a C# generic type constraint? 如何在F#中创建通用C#类型的实例? - How to create instance of a generic C# type in F#? C#泛型:如何指定类型约束并返回泛型类型的实例 - C# generics: How to specify type constraint and return instance of generic type 如何将多态性与泛型类型一起使用,该泛型类型的类型参数是 C# 中类型参数约束的子类型? - How do I use polymorphism with a generic type who's type argument is a child of the type parameter constraint in c#? 如何使用类型列表的参数创建泛型类型的实例 - How do I create an instance of a generic type with a parameter of type list
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM