在方法中途添加通用类型约束

4

我有两个通用方法 -

public CustomObject<T> MethodA<T>(T arg1) where T : class
{
    ...
    return MethodB<T>(arg1);
}


public CustomObject<R> MethodB<R>(R arg2) where R : class, IInterface
{
    ...
    return new CustomObject<R>();
}

问题显然是我不能使用未实现IInterface接口的类型(T)来调用MethodB。但如果我这样做呢 -
public CustomObject<T> MethodA(T arg1) where T : class
{
    ...
    var casted = arg1 as IInterface;
    if (casted != null)
    {
        return MethodB<T>(casted);
    }
}

显然这段代码无法编译通过,但是它应该能够编译成功,是吧?我该如何让编译器知道我知道casted实现了IInterface并且是一个类,所以调用MethodB是正确的呢?这里的大问题可能在于我试图返回CustomObject<T>

糟糕,我把这个搞砸了。给我一分钟重写一下。 - Nick
1
你有两个选择。要么在MethodB上删除类约束,要么在MethodA上添加IInterface约束。 - Mick
不幸的是,情况意味着我不能做这两件事。在此方法中没有办法“添加”T实现IInterface的信息吗? - Nick
请问您能否给出更多关于为什么需要这个结构的具体例子?如果没有更熟悉的上下文,比如了解您的方法做什么,或者包含这些方法的类做什么等等,就很难知道是否存在完全不同但有效的答案。 - ErikE
为什么在MethodA中只有一些对象是IActivatable而其他对象不是? - ErikE
显示剩余2条评论
2个回答

3
你需要使用反射来使其正常运行。
尝试这样做:
public CustomObject<T> MethodA<T>(T arg1) where T : class
{
    if (arg1 is IInterface)
    {
        var method = this.GetType().GetMethod("MethodB").MakeGenericMethod(arg1.GetType());
        return (CustomObject<T>)method.Invoke(this, new [] { arg1 });
    }
    return null;
}

2
@Nick - MineR的解决方案并不起作用。它使MethodB中的R始终为IInterface,而不是对象的实际类型。 - Enigmativity
2
@ErikE - 只有当速度太慢时才会有影响。而速度似乎并不能阻止人们在代码中放置大量的 try/catch 块。只有当缓慢成为问题时,速度才是重要的。 - Enigmativity
@Enigmativity 我并不是建议他不使用反射。我是建议他记住它的速度较慢,并且不要设计一个系统,在这个系统成长并达到临界点时,原本可以接受的速度变成了噩梦。速度在某种程度上始终很重要,因为速度是我们基本操作参数和SLA的一部分。如果知道这个方法将被多次调用,那么在这里进行反射是非常不明智的。也许在部署时应用程序只有1,000个数据点,但在1年内它将拥有1,000,000个数据点。这就是简单的计划过时。 - ErikE
@Servy,你仍然把我的陈述视为必须适用于解决方案。我所指的只是相对于正常调用,反射较慢。我认为读者(回答者?OP?)具有足够的经验和知识来自行决定是否重要。如果30倍的反射成本对他没有影响,那就没问题。如果有影响,那么他就知道可能需要一些缓存机制。你认为人们应该盲目地使用没有意识到潜在风险的答案吗?一个代码适用于所有情况吗?我的陈述是事实,并不需要我进行任何调查。人们进行了更多的解释。 - ErikE
@Servy 叹气。我并没有说答案是慢的。我说的是反射很慢。而且,正如我所说的,我是指与直接调用相比较而言。你为什么对此那么不满呢?至于缓存——我是指缓存每种类型的基于反射的方法创建。你以为我是指缓存输出值吗? - ErikE
显示剩余14条评论

-1
因为你的CustomObject<T>是泛型的,所以有问题。例如,CustomObject<object>不能与CustomObject<string>互换,但你可以在两者之间进行转换。
你可以通过以下方法解决这个问题:
public class CustomObject<T> where T : class {}
public interface IInterface { }

public static class CustomObjectConverter
{
    public static CustomObject<T1> ConvertTo<T1, T2>(CustomObject<T2> other)
        where T1 : class
        where T2 : class
    {
        return new CustomObject<T1>();
    }
}

public CustomObject<T> MethodA<T>(T arg1) where T : class
{
    if (arg1 is IInterface inf)
    {
        var b = MethodB(inf);
        return CustomObjectConverter.ConvertTo<T,IInterface>(b);
    }
    return null;
}
public CustomObject<T> MethodB<T>(T arg2) where T : class, IInterface
{
    return new CustomObject<T>();
}

1
这样做是不起作用的,因为它使得MethodB中的T始终为IInterface,而不是对象的实际类型。这与将签名设置为CustomObject<IInterface> MethodB(IInterface arg2)相同。 - Enigmativity
谢谢...让我检查一下。 - MineR
@Nick 我想他是对的,只有在你取消采纳我的回答之前,我才能删除它。 - MineR
好的,那一部分没问题,我提到的强制转换的注释是错误的。 - MineR
@Nick 如果是这样的话,那么方法一开始就不应该是通用的,因为您总是希望将接口作为类型提供。只需删除泛型并在使用泛型类型参数的任何地方使用接口即可。 - Servy
显示剩余3条评论

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