在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
}
}
可以使用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
和目标类型TypeA
或TypeB
之间没有关系(使用参数约束),并且我们只考虑类型转换问题,那么答案很简单:
不,没有比你正在使用的方法更好的方法了!!
我同意其他人的观点,如果你要对一个对象做更多的操作,你可能需要选择一个不同的设计
我知道这是一个很晚的问题,但是这里你可以做什么
一个很好的选择是让你的函数接受类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类型,您应该根据需要处理它。