在运行时添加通用约束?

13
我很困扰这个问题,如果有人有任何想法,请帮忙。我有通用的方法。
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<T **作为类**>();"? - Euphoric
发现得好 - 已编辑 :) - jcharlesworthuk
1
那你为什么不使用 if (typeof(T).IsClass) 呢? - Selman Genç
1
因为编译器不会读取那个逻辑,所以当我这样做时仍然会得到编译错误。Foo<T>(item) - jcharlesworthuk
@horrorcat 啊,抱歉现在我明白了,看起来你需要反射,用你的话说就是“作弊” :) - Selman Genç
哈哈哈!是的 - 这是作弊!:-P - jcharlesworthuk
4个回答

4

很遗憾,如果不更改Bar的泛型约束class或使用反射,就无法这样做。为了编译C#,必须在编译时知道T确实是一个class值。没有办法使用诸如typeof(T).IsClass的动态测试来满足此编译时约束。

您在问题中提到不能更改Bar,但似乎愿意接受动态失败的可能性。也许可以改变Foo而不具有该约束,而是在T不是类类型时引发异常。


嗨,感谢您的回答。我在“Foo”上放置了约束,因为我想使用“Foo”来实例化该项的新实例。因此,“Foo”本身确实需要采取一个对象,该对象可以通过执行“new T()”进行实例化。我宁愿在“Bar”中出现运行时异常,而不是在“Foo”中出现异常。但是,如果反射是唯一的选择,那么我想那也没问题 :) - jcharlesworthuk

4

没有反射的情况下调用Foo的唯一方法是将item向其层次结构中的一个类型/类强制转换(在进行适当的IsClass检查后)。

显然,您预先只知道其层次结构中的一个类型:Object

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

编辑:

另外,在其中一条评论中,您说您添加了 class 约束以实例化 T。您不需要这个,您需要的是 new 约束


哇,它实际上编译通过了,我完全忘记了你可以像这样“快捷方式”泛型,干杯!另外,是的,你是对的,我确实使用了新约束,但为了简化问题而将其省略了 :) - jcharlesworthuk
虽然这个解决方案对于某些情况很好,但请注意,调用Foo<object>(item)并不总是等同于调用Foo<T>(item)。 - MaMazav

0
if (typeof(T).IsClass)
{
    this.GetType()
        .GetMethod("Foo", System.Reflection.BindingFlags.Instance |
                          System.Reflection.BindingFlags.Public)
        .Invoke(this, new object[] { item });
}

我非常确定他明确地询问是否有反射的替代方法。 - Euphoric

-1

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

实际上,如果你将其包含在一个类中,你可以作弊:

    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<T>Foo<T>(T item) 不匹配,如果你将其更改为:Foo<T>(Container<T> item),那么它仍然会说 T 必须是引用类型。 - Tamir Vered
他说因为类型约束,new Container<T>(item) 这行代码无法编译。 - jcharlesworthuk

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接