【问题标题】:Adding generic constraints at runtime?在运行时添加通用约束?
【发布时间】:2014-02-10 06:45:46
【问题描述】:

我对此感到很困惑,所以如果有人有任何想法。我有通用方法

public void Foo<TClass>(TClass item) where TClass : class
{ }

我想从另一个泛型方法调用这个方法,但是这个泛型方法没有类型约束“where TClass : class”

public void Bar<T>(T item)
{
    this.Foo<T>(item);
} 

这不起作用,我收到错误

"类型 'T' 必须是引用类型才能将其用作参数 'TClass'"

我明白了。但我的问题是——如果它是一个类,我可以用 C# 语法做些什么来“过滤”泛型类型“T”以将其传递给“this.Bar”。有点像....

public void Bar<T>(T item)
{
    if (typeof(T).IsClass)
        this.Foo<T **as class**>();
} 

我意识到我可以使用反射来调用 Foo,但这似乎是作弊。我可以用 C# 做些什么来在运行时通过约束传递“T”吗?

另外 - 我无法更改方法“Bar”的约束,因为它来自接口,因此约束必须与接口上的约束匹配

【问题讨论】:

  • 你的意思是“this.Foo();” ?
  • 很好发现 - 已编辑 :)
  • 那你为什么不用if (typeof (T).IsClass) ??
  • 因为编译器不会读取该逻辑,所以我在执行此操作时仍然会出现编译错误。Foo(item)
  • @horrorcat 啊,对不起,现在我明白你了,看来你需要反思,用你的话作弊 :)

标签: c# generics constraints


【解决方案1】:

在没有反射的情况下调用Foo 的唯一方法是将item 转换为其层次结构中的类型/类之一(在正确的IsClass 检查之后)。

显然,您先验知道其层次结构中只有一种类型:Object

public void Bar<T>(T item)
{
    if (typeof(T).IsClass)
        this.Foo((object) item);
} 

编辑:

此外,在您所说的其中一个 cmets 中,您添加了 class 约束以实例化 T。你不需要那个,你需要的是new constraint

【讨论】:

  • 哇,真的可以编译了,我完全忘记了你可以像这样干杯的“快捷方式”泛型!是的,你是对的,我实际上也做了新的约束,但为了简化它,我把它排除在了这个问题之外:)
  • 虽然此解决方案适用于某些情况,但请注意调用 Foo(item) 并不总是等同于调用 Foo(item)。
【解决方案2】:

不幸的是,如果不将 Bar 更改为具有通用约束 class 或使用反射,则无法做到这一点。为了编译 C# 必须在编译时知道 T 确实是 class 值。无法使用诸如typeof(T).IsClass 之类的动态测试来满足此编译时间约束。

您在问题中提到您无法更改Bar,但您似乎愿意接受动态失败的可能性。也许改为将Foo 更改为没有约束,而是在T 不是类类型时抛出异常

【讨论】:

  • 您好,感谢您的回答。我将约束放在“Foo”上,因为我想使用“Foo”来实例化该项目的新实例。所以“Foo”本身确实需要一个可以通过执行“new T()”来实例化的对象。我宁愿在“Bar”而不是“Foo”中有一个运行时异常。但如果反射是唯一的选择,那么我想那很好:)
【解决方案3】:
if (typeof(T).IsClass)
{
    this.GetType()
        .GetMethod("Foo", System.Reflection.BindingFlags.Instance |
                          System.Reflection.BindingFlags.Public)
        .Invoke(this, new object[] { item });
}

【讨论】:

  • 我很确定他明确询问是否有替代反射的方法。
【解决方案4】:

我相信没有办法让它编译。您必须使用反射来进行调用。

其实。如果你将它包含在一个类中,你可能会作弊:

    public class Container<T>
    {
        public Container(T value)
        {
            Value = value;
        }

        public T Value { get; private set; }
    }

    public void Bar<T>(T item)
    {
        this.Foo<Container<T>>(new Container<T>(item));
    } 

但这增加了一层您需要调用的层,并使类型不太清晰。

【讨论】:

  • 这不起作用,因为Container&lt;T&gt;Foo&lt;T&gt;(T item) 不匹配,如果您将其更改为:Foo&lt;T&gt;(Container&lt;T&gt; item),那么它仍然会说T 必须是一个引用类型。
  • 他说new Container&lt;T&gt;(item) 行由于类型约束而无法编译
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-02-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多