在c#中将泛型类型参数强制转换为特定类型

本文关键字:转换 类型 泛型类型参数 | 更新日期: 2023-09-27 18:04:47

如果需要将泛型类型参数强制转换为特定类型,可以将其强制转换为对象,并执行如下强制转换:

void SomeMethod(T t)
{
    SomeClass obj2 = (SomeClass)(object)t;
}

是否有更好的方法来实现这一点,而不是将其转换为对象,然后再转换为特定类型?

问题:

我有一个接受泛型类型参数的泛型函数,在基于类型检查的函数内部,我做了如下操作:

    void SomeMethod(T t)
    {
        if (typeof(T).Equals(typeof(TypeA)))
        {
            TypeA = (TypeA)(object)t;
            //Do some operation
        }
        else if (typeof(T).Equals(typeof(TypeB)))
        {
            TypeB = (TypeB)(object)t;
            //Do some operation
        }
    }

在c#中将泛型类型参数强制转换为特定类型

可以使用Convert。ChangeType

SomeClass obj2 = (SomeClass)Convert.ChangeType(t, typeof(SomeClass));

使用as:

SomeClass obj2 = t as SomeClass;

这不会抛出异常,如果强制转换失败,t将为null。

我真的不知道你想做什么,但我希望你没有错过泛型的意义。

如果您的意图是将方法限制为类型SomeClass和后代:

void SomeMethod(T t)  where T : SomeClass

无意中发现了这个问题,并希望为后来发现这个问题的任何人提供一个更新的答案。在较新的c#版本(我写这篇文章时为8.0)中,模式匹配将允许您以更简洁的方式完成此操作:

void SomeMethod<T>(T t)
{
    switch(t)
    {
        case TypeA a:
            // Do some operation using a.
            Console.WriteLine($"{a} is a TypeA!");
            break;
        case TypeB b:
            // Do some operation using b.
            Console.WriteLine($"{b} is a TypeB!");
            break;
        default:
            // Handle this case.
            Console.WriteLine("I don't know what this type is.");
            break;
    }
}

这将检查对象的类型,如果找到匹配,将在一步中将其赋值给指定的变量,然后在该情况的主体中可用。(您也可以在if语句中执行类似的操作:if (t is TypeA a) a.DoSomething(); ...)

说了这么多,我确实同意其他的回应,你应该尽可能地约束这一点(void SomeMethod<T>(T t) where T : ISomeInterface {...})或移动操作到你正在测试的类,如果可能的话。

更好的设计是在类型T和您希望在方法中期望的类(在本例中为SomeClass)之间添加一个常见的约束。

class SomeConsumer<T> where T : ISomeClass
{
    void SomeMethod(T t)
    {
        ISomeClass obj2 = (ISomeClass) t;
    }
}
interface ISomeClass{}
class SomeClass : ISomeClass {}

基于问题的编辑

那是糟糕的设计。尝试将该"操作"移到类本身中,以便调用者不必知道类型。如果这是不可能的,共享更多正在做的事情,你想要完成的是,你没有一个If/else语句堆栈,其中执行取决于传递给方法的对象类型。

class SomeConsumer<T> where T : ISomeClass
{
    void SomeMethod(T t)
    {
        ISomeClass obj2 = (ISomeClass) t;
        // execute
        t.Operation();
    }
}
interface ISomeClass{
    void Operation();
}
class SomeClass : ISomeClass {
    public void Operation(){/*execute operation*/}
}

这种情况可以使用as

void SomeMethod(T t)
{
    SomeClass obj2 = t as SomeClass;
}

如果输入类型T和目标类型TypeATypeB之间没有关系(使用参数约束),并且我们只考虑类型转换问题,那么答案很简单:

不,没有比你正在使用的方法更好的方法了!!

我同意其他人的观点,如果你要对一个对象做更多的操作,你可能需要选择一个不同的设计

我知道这是一个很晚的问题,但是这里你可以做什么

一个很好的选择是让你的函数接受类object的参数,并做你想要的切换情况

然后进行选角

YourClass = (YourClass) parameterObject;

这个简单的hack就能解决所有问题吗?:

    public static TOut Cast<TOut, TIn>(TIn item) 
    {
        return Enumerable.Cast<TOut>(new[] { item }).First();
    }

是的,这样编译器就找不到类型转换问题,我们创建的对象也会让GC有点压力,但它很容易编写和理解。

当然,你可以在Cast操作符的实现中只复制你需要的位,但我猜大多数人不会理解代码的内容。所以,如果你需要更多的性能,你可以得到更多,但这应该在大多数情况下工作良好。

我有一个继承自另一个类a的类b,以及Convert。更改类型失败,提示"对象必须实现不可转换"。System.InvalidCastException。但我可以用不同的方式来做:

public classs ClassB : ClassA
{
    public string SomePropety { get; set; }
}
public class ClassA
{
    public string SomeField { get; set; }
}
public class SomeClass
{
   public T AGenericMethod<T>()
   {
       var retObj = SomeClassWithGeneric.SomeGenericMethod<T>();
       object obj = retObj;
       ClassA objA = (ClassA)obj;
       //Do stuff with objA....
       return  retObj;
   }
}
对我来说,这里的技巧是首先将var retObj设置为一个对象变量,然后我可以将对象变量强制转换为我想要的基类。这比我能做的任何其他hack都要简单和干净得多!

这似乎很容易微软。

其他解决方案也不错。虽然我建议这个解决方案,因为我发现它非常干净,易于使用。

if (t is TypeB tb)
{
    // tb can be used as a variable of type TypeB
    tb.SomeTypeBMethod();
}
else
{
    throw new Exception("t is not of type TypeB");
}

抛出异常只是一个例子。如果t不是TypeB类型,您应该根据需要处理它。