C# 泛型类型约束:如何创建此C文档中描述的类的实例

C# 泛型类型约束:如何创建此C文档中描述的类的实例,c#,C#,上述引用文件的相关文本为: 例如,可以声明泛型类MyGenericClass,以便类型参数T实现IComparable接口: 以下是MyGenericClass,从上面引用的文档中逐字复制: public class AGenericClass<T> where T : IComparable<T> { } 在您的特定示例中,AGenericClass中的泛型参数T和相应的约束(其中T:IComparable)指定泛型参数T必须实现IComparable接口,这对于您的

上述引用文件的相关文本为:

例如,可以声明泛型类MyGenericClass,以便类型参数T实现IComparable接口:

以下是MyGenericClass,从上面引用的文档中逐字复制:

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

在您的特定示例中,AGenericClass中的泛型参数T和相应的约束(其中T:IComparable)指定泛型参数T必须实现IComparable接口,这对于您的问题并不重要,但对于理解正在发生的事情很重要,因为此接口本身是泛型的。请注意,IComparable中的T与AGenericClass中的T相同-也就是说,您将限制为实现IComparable本身的类型-字符串是一个很好的示例,因为它是IComparable。但是,您的MyStringCompparable不是IComparable-它是一个IComparable,它违反了AGenericClass上的泛型类型约束(如定义)

这里的解决方案是使用2个通用参数—一个用于容器,另一个用于包含的类型:

类代理类,其中TContainer:IComparable{} 实例化如下:

var stringComparer=新的代理类; var stringComparer2=新的代理类; 根据更新的问题和评论编辑:

如果你真的想去掉第二个泛型参数,必须给出一些东西-例如,我们可能会失去指定容器的能力,使其成为传递容器,类似于System.Collections.generic.LinkedList的实现-LinkedListNode是一个传递容器,这是LinkedList类已知的,并且不能换成其他类型的容器:

公共类选择器//完全没有约束 { 私有可选择容器;//容器始终是可选择的 公共选择项 =>容器=新的可选{Item=Item}; } 用法更简单:

变量选择器=新选择器或某些数据;//不需要通过这里
但是,您现在只能使用可选的作为容器—您无法使更好的可选的容器作为容器互换使用。基本上,它可以归结为您的用例-您需要两个变量-一个容器和一个包含的数据,或者您只需要一个变量-仅包含的数据就可以了。在前一种情况下,您必须有2个泛型参数,因为您有2个变量;在后一种情况下,您只需要一个泛型参数,因为您有一个变量和一个常量,即容器。

在您的特定示例中,AGenericClass中的泛型参数T以及相应的约束,其中T:IComparable指定泛型参数T必须实现IComparable接口,这对于您的问题并不重要,但对于理解正在发生的事情很重要,因为该接口本身是泛型的。请注意,IComparable中的T与AGenericClass中的T相同-也就是说,您将限制为实现IComparable本身的类型-字符串是一个很好的示例,因为它是IComparable。但是,您的MyStringCompparable不是IComparable-它是一个IComparable,它违反了AGenericClass上的泛型类型约束(如定义)

这里的解决方案是使用2个通用参数—一个用于容器,另一个用于包含的类型:

类代理类,其中TContainer:IComparable{} 实例化如下:

var stringComparer=新的代理类; var stringComparer2=新的代理类; 根据更新的问题和评论编辑:

如果你真的想去掉第二个泛型参数,必须给出一些东西-例如,我们可能会失去指定容器的能力,使其成为传递容器,类似于System.Collections.generic.LinkedList的实现-LinkedListNode是一个传递容器,这是LinkedList类已知的,并且不能换成其他类型的容器:

公共类选择器//完全没有约束 { 私有可选择容器;//容器始终是可选择的 公共选择项 =>容器=新的可选{Item=Item}; } 用法更简单:

变量选择器=新选择器或某些数据;//不需要通过这里 但是,您现在只能使用可选的作为容器—您无法使更好的可选的容器作为容器互换使用。基本上,它可以归结为您的用例-您需要两个变量-一个容器和一个包含的数据,或者您只需要一个变量-仅包含的数据就可以了。在前一种情况下,您必须有2个泛型参数,因为您有2个变量;在后一种情况下,您只需要一个泛型参数,因为您有一个变量和一个常量,即容器

为了清楚起见,我的问题是如何创建AGenericClass的实例,正如引用的C文档中所定义的那样

通过为类型段提供类型 满足约束T:i可比较的仪表T。在您的代码示例中:

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
}
让我们一次测试一个。假设我们将T设置为MyStringComparable。为了满足约束,该类型需要实现IComparable,其中T是您作为类型参数提供的类型。因为您将T设置为mystringcompable,所以mystringcompable需要实现IComparable

是吗?不,没有。它实现了其他类型参数T的IComparable,即string

为了满足约束,您需要这样的内容:

公共类MyStringComparable:IComparable { public int compareTommyString Comparable other=>抛出新的NotImplementedException; } 好的,第二个呢。使用MyComparable作为类型参数,执行相同的测试。当然,您需要为该类提供一个类型参数,因为它是泛型的。在您的示例中,使用字符串作为类型参数,因此它是MyComparable的

根据约束,类型参数MyComparable需要实现IComparable。是吗?不,没有。MyComparable类仅实现IComparable。同样,这是错误的约束

实现正确约束的类看起来更像这样:

公共类MyComparable:IComparable { public int compareTomomcomparable other=>抛出新的NotImplementedException; } 泛型类型约束可能很难处理。人们经常纠结的另一个领域是试图继承或以其他方式使用具有约束的现有类型参数,而忘记了他们需要在该上下文中满足约束。这通常是通过在它们自己的泛型类或方法中重复约束来实现的

关于你的问题中的这段文字,我也会注意到:

另外,我的问题与IComparable或comparing对象无关——这恰好是示例代码中的接口

事实上,你的问题至少与IComparable有一定的关系,因为它经常以这种递归的方式使用,让人感到困惑。如果采用更传统的方案,您不会遇到同样的问题

您遇到的场景是C++奇怪的循环模板模式的C变体。Eric Lippert的文章,您可能希望在探索和了解泛型类型参数及其约束时阅读

为了清楚起见,我的问题是如何创建AGenericClass的实例,正如引用的C文档中所定义的那样

通过为满足约束T:IComparable的类型参数T提供类型。在您的代码示例中:

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
}
让我们一次测试一个。假设我们将T设置为MyStringComparable。为了满足约束,该类型需要实现IComparable,其中T是您作为类型参数提供的类型。因为您将T设置为mystringcompable,所以mystringcompable需要实现IComparable

是吗?不,没有。它实现了其他类型参数T的IComparable,即string

为了满足约束,您需要这样的内容:

公共类MyStringComparable:IComparable { public int compareTommyString Comparable other=>抛出新的NotImplementedException; } 好的,第二个呢。使用MyComparable作为类型参数,执行相同的测试。当然,您需要为该类提供一个类型参数,因为它是泛型的。在您的示例中,使用字符串作为类型参数,因此它是MyComparable的

根据约束,类型参数MyComparable需要实现IComparable。是吗?不,没有。MyComparable类仅实现IComparable。同样,这是错误的约束

实现正确约束的类看起来更像这样:

公共类MyComparable:IComparable { public int compareTomomcomparable other=>抛出新的NotImplementedException; } 泛型类型约束可能很难处理。人们经常纠结的另一个领域是试图继承或以其他方式使用具有约束的现有类型参数,而忘记了他们需要在该上下文中满足约束。这通常是通过在它们自己的泛型类或方法中重复约束来实现的

关于你的问题中的这段文字,我也会注意到:

另外,我的问题与IComparable或comparing对象无关——这恰好是示例代码中的接口

事实上,你的问题至少与IComparable有一定的关系,因为它经常以这种递归的方式使用,让人感到困惑。如果采用更传统的方案,您不会遇到同样的问题

您遇到的场景是C++奇怪的循环模板模式的C变体。Eric Lippert写的,你可能喜欢在探索和学习的过程中阅读

关于泛型类型参数及其约束。

您实际上需要两个类型参数来完成您要做的事情-因为MyComparable是一个容器,它本身不是一个可比较的对象,而只是它所包含的类型。我仍然不明白。考虑到实例化AGenericClass的目标,您是否介意写一个简短的示例。我根据您的编辑和我们对我的答案的评论线程更新了我的答案。如果您能查看您的帖子,这会很有帮助,因为您的文本暗示您要提供指向两个不同堆栈溢出问题的链接,这篇文章的第一个链接就是C。另外,您将一个答案中的代码复制到了问题中,但没有解释为什么这样做会以某种方式改进问题。你在那里的意图是什么?请不要包含答案中的代码,除非这样做是为了详细说明和澄清您实际遇到的问题。实际上,您需要两个类型参数来完成您要做的事情-因为MyComparable是一个容器,它本身不是可比的,而只是它所包含的类型。我仍然不明白。考虑到实例化AGenericClass的目标,您是否介意写一个简短的示例。我根据您的编辑和我们对我的答案的评论线程更新了我的答案。如果您能查看您的帖子,这会很有帮助,因为您的文本暗示您要提供指向两个不同堆栈溢出问题的链接,但是第一个这样的链接只是对C文档的引用,与您在文章顶部使用的链接相同。另外,您将一个答案中的代码复制到了问题中,但没有解释为什么这样做会以某种方式改进问题。你在那里的意图是什么?请不要包含答案中的代码,除非这样做是为了详细说明和澄清您实际遇到的问题。因此,我认为您是说第二个类型参数描述了第一个参数的类型。这是有道理的,也是一个很好的解决方案。然而,我很想知道微软认为如何使用代码中显示的一个参数来实现这一点。在这一点上,这纯粹是一个学术问题。@Sam第二个类型参数TData描述了第一个类型参数TContainer所描述的容器中的包含类型。注意,它们必须匹配-不会生成新的AGenericClass。如果您没有额外的包装器/容器,Microsoft的示例就可以工作——例如,根据约束条件,您的原始代码将与新的AGenericClass一起工作。这里的问题特别是容器的介绍,它本身就是一个类型,当用作泛型参数时,会受到约束检查。我已经更新了我的问题,以包括与我的业务问题匹配的代码。这里的冗余更为明显,因为我们正在处理一个T实例,并用一个具有bool属性的类包装它。是否可以使用单个类型参数创建此参数?Re:注意,它们必须匹配——那么为什么我们需要两个参数呢?我们如何用一个参数实现这一点?@Sam在您的用例中,我认为您无法排除第二个通用参数;为了消除它,但保持包装器的可选性,您需要在容器上使用非通用接口;比如说,在您的情况下是可选择的,或者是非泛型基类。然后,选择器上的T可以约束为ISelectable,从而分解传入的ISelectable中包含的泛型类型。这种方法的问题是访问ISelectable中的T项-无法将其移动到该接口,因为它是一个泛型属性,您只能移动bool IsSelected…@Sam…如果这在您的用例中起作用-例如,您有方法访问泛型数据,这些数据本身可以由非泛型接口或基类描述,然后可以消除第二个泛型参数;但是,如果您必须能够访问选择器类中的T项,并且必须能够指定可选择的容器类型,则无法避免第二个泛型参数。因此,我认为您是说第二个类型参数描述了第一个参数的类型。这是有道理的,也是一个很好的解决方案。然而,我很想知道微软认为如何使用代码中显示的一个参数来实现这一点。在这一点上,这纯粹是一个学术问题。@Sam第二个类型参数TData描述了第一个类型参数TContainer所描述的容器中的包含类型。注意,它们必须匹配-不会生成新的AGenericClass。如果您没有额外的包装器/容器,Microsoft的示例就可以工作——例如,根据约束条件,您的原始代码将与新的AGenericClass一起工作。这里的问题特别是容器的介绍,它本身就是一个类型,当用作泛型参数时,会受到约束检查
对与我的业务问题相匹配的代码进行反编码。这里的冗余更为明显,因为我们正在处理一个T实例,并用一个具有bool属性的类包装它。是否可以使用单个类型参数创建此参数?Re:注意,它们必须匹配——那么为什么我们需要两个参数呢?我们如何用一个参数实现这一点?@Sam在您的用例中,我认为您无法排除第二个通用参数;为了消除它,但保持包装器的可选性,您需要在容器上使用非通用接口;比如说,在您的情况下是可选择的,或者是非泛型基类。然后,选择器上的T可以约束为ISelectable,从而分解传入的ISelectable中包含的泛型类型。这种方法的问题是访问ISelectable中的T项-无法将其移动到该接口,因为它是一个泛型属性,您只能移动bool IsSelected…@Sam…如果这在您的用例中起作用-例如,您有方法访问泛型数据,这些数据本身可以由非泛型接口或基类描述,然后可以消除第二个泛型参数;但是,如果您必须能够访问选择器类中的T项,并且必须能够指定可选择的容器类型,则无法避免第二个泛型参数。谢谢您的评论,Peter。>修复错误的方法是提供一个确实满足约束的类型。好的,我们怎么做?您能否提供编译、运行并显示实际实例化的AGenericClass的示例代码?>您需要这样的代码才能满足约束:。。。。。。。。。我尝试了提供的代码,但它无法生成。请不要被MyStringComparable或MyComparable所束缚。这些只是我回答问题的尝试。>但想必你的问题真的应该用这样的措辞。。。。不,原来的问题并没有排除这一点,所以如果你知道怎么做,请分享。@Sam:你能提供编译、运行并显示实际实例化的AGenericClass的示例代码吗?-只要像我上面描述的那个样正确地定义了带有约束的泛型类,就可以使用其他用户定义的类型作为类型参数来实例化AGenericClass。例如,new AGenericClass或new AGenericClass,就像您已经尝试过但失败了一样。错误的不是试图实例化AGenericClass的代码,而是其他类型的声明。谢谢您的评论Peter。>修复错误的方法是提供一个确实满足约束的类型。好的,我们怎么做?您能否提供编译、运行并显示实际实例化的AGenericClass的示例代码?>您需要这样的代码才能满足约束:。。。。。。。。。我尝试了提供的代码,但它无法生成。请不要被MyStringComparable或MyComparable所束缚。这些只是我回答问题的尝试。>但想必你的问题真的应该用这样的措辞。。。。不,原来的问题并没有排除这一点,所以如果你知道怎么做,请分享。@Sam:你能提供编译、运行并显示实际实例化的AGenericClass的示例代码吗?-只要像我上面描述的那个样正确地定义了带有约束的泛型类,就可以使用其他用户定义的类型作为类型参数来实例化AGenericClass。例如,new AGenericClass或new AGenericClass,就像您已经尝试过但失败了一样。错误的不是试图实例化AGenericClass的代码,而是其他类型的声明。
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
}