将返回值强制转换为通用类型

13
假设我们有一个只有一个泛型方法的接口:
public interface IExtender
{
    T GetValue<T>(string tag);
}

并且有一个简单的实现A,它根据“tag”参数返回两种不同类型(B和C)的实例:

public class A : IExtender
{
    public T GetValue<T>(string tag)
    {
        if (typeof(T) == typeof(B) && tag == null)
            return (T)(object) new B();
        if (typeof(T) == typeof(C) && tag == "foo")
            return (T)(object) new C();
        return default(T);
    }
}

有没有可能避免双重转换 (T)(object) 呢?或者,是否有一种方法告诉编译器:“嘿,我确定这个转换在运行时不会失败,让我直接转换而不是先转换为对象!”


为什么需要(T)(Object)转换?您不能直接(T) new C() 吗? - Anuraj
@Anuraj:不是的 - 这就是问题的全部。请阅读我回答中引用的博客文章。 - Jon Skeet
6个回答

10

或者,有没有一种方法可以告诉编译器“嘿,我确定这个转换在运行时不会失败,让我直接进行转换而不是先转换为对象!”

不,该语言是故意设计成防止这种情况发生的。Eric Lippert最近写了一篇博客谈到了这个问题。我同意这很烦人,但确实有一定的道理。

说实话,像这样的“通用”方法通常都有点设计上的问题。如果一个方法必须针对各种不同类型有特殊情况,您至少应该考虑使用单独的方法代替。(GetBGetC


是的。在这里,GetB、GetC和GetEtc不是一个选项,但我不知道这是否是讨论设计的正确场所。无论如何,第一段和链接就是我要找的答案。谢谢。 - fog
1
是的,这是可能的,有多种方式可以实现,有时这是解决设计问题的唯一好方法。这就是为什么引入了动态关键字和Convert.ChangeType。 - Dino Dini
@DinoDini:我认为这两个选项都没有实际做到OP所说的。 OP并不想执行任何实际值转换-他们只是想避免双重转换。有效地使用动态只是使转换为“对象”隐式进行,但它并没有以任何有用的方式去除它。 - Jon Skeet
博客链接已失效,但我在这里找到了它:https://ericlippert.com/2012/07/10/696/ - Louise Eggleton
@LouiseEggleton:谢谢,我已经编辑过了。将来遇到类似的情况,随时可以直接建议修改。 - Jon Skeet

6
public T MyMethod<T>(string tag) where T : class
    {
        return new A() as T;
    }

正是我所需要的! - shieldgenerator7
这个方法如果不限制T为一个类的话是行不通的。另一个解决方案(如下)使用dynamic关键字就没有这样的限制。 - undefined

3
检查这个样例:
    public T GetValue<T>(string tag) where T : class, new()
    {
        if (typeof(T) == typeof(B) && tag == null)
            return new T();
        if (typeof(T) == typeof(C) && tag == "foo")
            return new T();
        return default(T);
    }

不需要强制类型转换,你可以创建“T”类的实例,只需添加一般约束,说明T是一个类,并且它有无参数构造函数,这样就不需要创建另一个基础类型,并且可以确保只有适当的类型通过此通用方法。


1
这就是当你(其实是我)试图简化代码以作为示例发布时发生的情况。在真正的代码中,实例已经存在,我无法对T加任何约束。所以是的,这个方法可以运行(谢谢!),但并没有解决我的问题。 - fog

2
你可以使用动态类型来存储真实结果,但必须确保泛型参数类型是正确的返回类型。
TResult GetResult<TResult>() 
{
    dynamic r = 10;
    return r;
}

1
页面底部有实际答案。再次提醒。 - Dino Dini
2
这并不真正地移除强制类型转换 - 它只是将其从显式变为隐式。它也可能比双重强制转换表现得更差。 - Jon Skeet
这对我来说是一个解决方法。如果程序员自己检查类型,我不认为会有问题。 - Rudy Scoggins

0
如果您让B和C实现相同的接口,您可以在T上使用type constraint。这可能不完全是您想要的,但正如其他人建议的那样,您想要的并不是真正可能的。
public interface IclassBndC {}

public class B : IclassBandC {}

public class C : IclassBandC {}

public class A : IExtender
{
    public T GetValue<T>(string tag) where T : IclassBandC 
    {
        if (tag == null)
            return new B();
        if (tag == "foo")
            return new C();
        return default(T);
    }
}

请注意,这需要您能够修改“B”和“C”。 - O. R. Mapper
@O.R.Mapper 是的,我错过了提到这是不可能的吗? - Bazzz
你没有提到BC是由OP编写而不是预定义和封闭的,但我认为应该提到这个限制。 - O. R. Mapper

0
不,这是不可能的。唯一的方法是让编译器了解关于 T 的其他假设。正如 泛型参数约束列表 所证明的那样,在 C# 中没有定义任何约束来要求特定转换的可用性。

可以通过动态类型转换实现。 - Dino Dini

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