【问题标题】:Is it possible to use/extract the type used by a generic to define a second generic's type?是否可以使用/提取泛型使用的类型来定义第二个泛型的类型?
【发布时间】:2017-04-11 20:13:50
【问题描述】:

我知道标题可能有点不清楚,所以我会解释我要做什么。

请注意,这更多是出于语言功能的教育原因。换句话说如果这是可能的,而不是应该这是解决它的方法。

考虑以下泛型类:

public class Foo<TId>
{
    TId Id { get; set; }
}

现在考虑基于上述的具体子类。下面是两个使用 'int' 和 'string' 的例子...

public class IntFoo : Foo<int>
{
}

public class StrFoo : Foo<string>
{
}

最后是一个泛型,它接受一个 Foo 作为类型参数,并继承自一个从 Foo 接受其类型参数的 Laa。

public class BaseClass<TFoo, TFooId> : Laa<TFooId>
{
}

public class Laa<TFooId>
{
}

这是基于 int 和字符串的方法,但请注意,除了 IntFoo 和 StrFoo,我还必须明确定义 int 和 foo...

public class IntFinal : BaseClass<IntFoo, int>
{
    char somePropSpecificToIntFinal{ get; set; }
}

public class StrFinal : BaseClass<StrFoo, string>
{
    char somePropSpecificToStrFinal{ get; set; }
}

请注意,这些“最终”类是具有自己属性的具体类型,不能简化为采用类型的泛型(即使用具有单一类型 T 的泛型,然后子类化另一个采用 Foo 和T 作为它的参数。

我想知道是否有一种方法可以推断出该类型,以便可以这样编写...

public class IntFinal : BaseClass<IntFoo>
{
}

public class StrFinal : BaseClass<StrFoo>
{
}

...并具有从 Foo 指定的泛型隐含的 Laa 类型。这是我想要的伪代码示例。

public class BaseClass<TFoo> : Laa<TFoo.IdType>
{
}

那么在 C# 中可能吗?

注意,如果这不能用类来完成,可以用接口来完成吗?

考虑一下...

interface IFoo
{
    Type FoosType { get; }
}

public class Foo<TId> : Foo
{
    TId Id { get; set; }

    Type FoosType { get{ return TId } }
}

那就这样吧……

public class BaseClass<TFoo> : Laa<TFoo.FoosType>
where TFoo : Foo
{
}

(注意:从技术上讲,FoosType 必须是静态的,并且您不能再次使用静态继承,这是伪代码。)

如果将 TFoo 限制为 IFoo,那么在定义 Laa 时是否可以使用 'FoosType' 作为类型说明符?

【问题讨论】:

  • 我想我见过一些这样的实现。但不确定。
  • 我认为您应该能够使用 Where 子句 (where IntFoo : Int32) 或 (where IntFoo : INumeric) 来处理这个问题 - 想法在 MSDN
  • 不,我没有将其限制为数字。我正在尝试获取 Foo 使用的任何类型。为了清楚起见,我会更新问题。
  • 你可以拥有最终的泛型类 public class Final&lt;T&gt; : BaseClass&lt;Foo&lt;T&gt;, T&gt; 但是在这种情况下你应该忘记非泛型 IntFooStrFoo 并尝试以泛型方式实现它们。
  • 好点,但是当您尝试简化示例时,这就是问题所在。我需要具体的 IntFoo 和 StrFoo 因为它们都有独特的属性。实际上,它们是 CategoryRow 和 ComponentRow,它们都基于 DBRow,在这种情况下 TId 是 int,但在另一种情况下可能是 GUID。我正在构建一个基于该 ID 进行检索的缓存机制(Laa 实际上是 Cache)。

标签: c# generics types


【解决方案1】:

根据 C# 规范,您无法做到这一点。类型推断目前仅适用于方法,不适用于类型(如您的案例的类)

打破您所需结果的第二条规则是,您不能指定一个泛型类型参数并推断另一个,它是 Provide all 或 Infer all 方法的情况。

C# 规范:

1.6.3 类型参数

当使用泛型类时,必须为每个类型参数提供类型实参

【讨论】:

  • 但是我要求的不是明确提供吗?我没有根据你提到的原因推断任何事情。换句话说,如果我正在定义一个泛型,我是否可以获取传递给它的类型以传递给基类。其实……我只是有个主意!一分钟后回来......
  • @MarqueIV 不,您没有明确提供它。如果一个类需要 2 个类型参数,则必须指定这两个:Foo&lt;T, V&gt;。您不能指定T 并让编译器通过其他方式推断V。这是语言规范。
  • 我们在说不同的事情。如果你有一个带有属性 Laa 的 Foo,并且你传递了一个 Foo,你可以抓住 Laa 并将它显式地传递给其他东西。这基本上就是我要问的,除了它不是一个属性,而是一个已经显式传入的类型。没有什么是隐含的。
【解决方案2】:

您的问题不是很具体,也不清楚实际的限制和要求是什么。也就是说……

类型推断只发生在泛型方法上,而不是泛型类型。所以从字面上理解你的问题,答案是否定的,没有办法推断类型。

可能对你有用的是在类定义中使用Foo&lt;TId&gt; 而不是IntFoo

class BaseClass<TFooId> : Laa<TFooId>
{
    public Foo<TFooId> Method() { ... }
}

(当然,您可以在任何适当的地方应用类型:字段、属性类型等)

即而不是使用两个类型参数编码BaseClass 类型,只需使用唯一定义您正在使用的Foo&lt;TFooId&gt; 基类的有趣/有用元素的那个,然后使用该基类型而不是更多派生的@ 987654327@

在您的示例中,您对 TFoo 类没有任何限制,因此 BaseClass&lt;TFoo, TFooId&gt; 无论如何都不能使用来自 Foo&lt;TId&gt; 的基类型类成员。但是,即使您确实打算将 TFoo 限制为 Foo&lt;TFooId&gt;,您似乎也不需要指定该类型。

如果以上内容没有用,那么您需要为您的问题添加更多细节,以准确解释需要什么。还要考虑一下,很可能人们已经走上了这条路,如果您对认为需要的实现机制表达较少的问题,而是在更高的层次上表述它,你可能会可能会在 Stack Overflow 上找到现有问题或其他地方的文章已经解决了这个更广泛的问题。

至少,如果您自己无法找到此类参考资料,那么以这种方式表达您的问题可能会更快地产生更好的答案。

另见XY Problem

【讨论】:

  • 虽然我很欣赏你的回答,但我已经解释了为什么我不能使用你所说的基类实现(使用单一类型而不是两种类型的泛型。)以及解释细节,这个问题会比这长得多,而且并不是我真正想要的,而是看看你是否可以从继承自泛型的东西中提取类型。我相信答案是否定的,但总有部分语言你不知道别人在哪里,所以我问了。不过谢谢你的批评。我们只是在看两种不同的东西。
猜你喜欢
  • 1970-01-01
  • 2014-08-19
  • 1970-01-01
  • 2011-09-15
  • 1970-01-01
  • 2013-03-01
  • 1970-01-01
  • 2019-07-21
  • 1970-01-01
相关资源
最近更新 更多