我有这样的代码:
class Base { }
class Derived : Base { }
class Wrapper<T> {
public T Value { get; }
public Wrapper (T value) { Value = value; }
}
我想像这样使用Wrapper
:
Wrapper<Base> wrapper = new Wrapper<Derived> (new Derived ());
但它最终会出现这个错误:
错误CS0029无法隐式转换类型&#39; Wrapper&lt; Derived&gt;&#39;到&#39; Wrapper&lt; Base&gt;&#39;
我尝试在Wrapper
class
中创建一个充当转换器的方法
public Wrapper<TResult> To<TResult> () /* Constraints needed here. */ =>
new Wrapper<TResult> (Value);
但我错过了一些有效的约束。当前代码最终出现错误:
S1503参数1:无法转换为&#39; T&#39; to&#39; TResult&#39;
我认为To
方法的约束可能看起来像where T : TResult
,但这不是有效约束。
有哪些方法可以轻松地将转换器从Wrapper<Derived>
实现到Wrapper<Base>
?
答案 0 :(得分:4)
您可以像这样使用协方差:
class Base { }
class Derived : Base { }
interface IWrapper<out T>
{
T Value { get; }
}
class Wrapper<T> : IWrapper<T>
{
public T Value { get; private set; }
public Wrapper(T value) { Value = value; }
}
class Program
{
static void Main(string[] args)
{
IWrapper<Base> wrapper = new Wrapper<Derived>(new Derived());
}
}
答案 1 :(得分:3)
首先,我会向类添加一个约束,要求T
必须是Base
类型:
class Base { }
class Derived : Base { }
class Wrapper<T> where T : Base // T must be (derived from) Base
{
public T Value { get; }
public Wrapper (T value) { Value = value; }
}
其次,通用转换器危险。如果有人试图将Wrapper<Gnu>
转换为Wrapper<Lion>
?
所以我退一步做一个非通用的转换器,转换为Wrapper<Base>
:
public Wrapper<Base> ToBase()
{
return new Wrapper<Base>(Value);
}
这是有效的,因为T
在类级别的约束。
C#实际上是一种高级别安全性的语言。但是你可以通过省略任何约束并试图抛出任何内容来绕过它并在评论中做你要求的事情:
public Wrapper<TResult> To<TResult>() where TResult : class
{
return new Wrapper<TResult>(Value as TResult);
}
您需要class
约束和as
运算符,因为两个通用参数之间的直接转换不可编译(因为IL过分依赖于特定类型)。
但是如果类型不匹配,这将返回Wrapper
个Value
个实例,设置为null
。它也适用于派生类型而不是基类型。所以要小心。您可以为此添加一些额外的检查。并照顾gnus:)
更新:
更安全的方式:
public Wrapper<TResult> To<TResult>() where TResult : class// TResult must also be (derived from) Base
{
if (!typeof(TResult).IsAssignableFrom(typeof(T)))
throw new InvalidCastException();
return new Wrapper<TResult>(Value as TResult);
}
这会检查T
是否来自TResult
,如果没有,则会引发InvalidCastException
。您可以根据自己的需要进行优化。
答案 2 :(得分:0)
您遇到的问题是泛型类型Wrapper<Base>
和Wrapper<Derived>
是.NET Framework的两个完全不同的类
您可以做的是创建Wrapper
类型的新Base
:
Wrapper<Base> wrapper = new Wrapper<Base>(new Derived());
或完成您的To方法方法:
public Wrapper<TResult> To<TResult>() where TResult : T
=> new Wrapper<TResult>( (TResult)Value ); // This could throw an error
public bool TryCastTo<TResult>(out Wrapper<TResult> derivedWrapper) where TResult : T
{
derivedWrapper = null;
// EDIT: changed to the version from René Vogt since this is much cleaner and mine had a little error
if (!typeof(T).IsAssignableFrom(typeof(TResult)))
{
return false;
}
derivedWrapper = new Wrapper<TResult>( (TResult)Value );
return true;
}
用法是:
Wrapper<Derived> derivedWrapper1 = wrapper.To<Derived>();
Wrapper<Derived> derivedWrapper2;
bool success = wrapper.TryCastTo<Derived>(out derivedWrapper2);