C# 和强制转换泛型参数
本文关键字:泛型 参数 转换 | 更新日期: 2023-09-27 18:35:41
我来自C++模板编程,有时会对泛型感到非常困惑。由于没有方法专用化,我尝试使用铸造。这是我所拥有的:
public interface INonGen
{
void Get<T>(ref T value);
}
public interface IGen<U> : INonGen
{
}
public class Gen<U> : IGen<U>
{
private U u;
public void Get<T>(ref T value)
{
if (value is U)
{
value = (T) u;
}
else
throw new Exception();
}
}
这不会编译。
我有办法制作这个演员表吗?
我想要这个的原因是:使用C++模板,我会对支持的类型进行专用化,以及引发异常的非专用版本。
基本思想是这样的:具有泛型方法的非泛型接口。尝试使用正确的类型获取值应该有效,尝试使用错误的类型可能会引发。
我们应该保持类型安全,所以我需要返回一个正确类型的实例/值。对象上的任何快捷方式都是不可接受的,在非泛型接口中约束类型也是不可接受的。
通用实现是为了避免重复。我想支持多种不同的类型(但只有一小部分类型),但我希望在实例化一个类时决定这一点(并定义 T 的含义);我仍然希望非通用接口允许使用任何 T 进行访问;也就是说,我不希望在界面中显式使用类型集。
将一个对象强制转换为另一个对象时,如果编译器找不到转换,则会报告错误。由于这两个类型参数不受约束,因此唯一的选择是使用 as
运算符,该运算符不是抛出InvalidCastException
,而是在强制转换失败时返回null
。若要使用 as
还需要将泛型类型限制为类。
public class Gen<U> : IGen<U>
{
private U u;
public void Get<T>(ref T value)
where T : class
{
if (value is U)
{
value = u as T;
}
else
throw new Exception();
}
}
如果不想添加约束,可以强制转换为Object
:
value = (T)(object)u;
不过,您的代码中存在逻辑错误。如果value is U
,什么保证u is T
?例如:
var gen = new Gen<Base>();
gen.Set(new DerivedA()); // sets u;
var b = new DerivedB();
gen.Get(ref b);
在这种情况下value is Base
但不u is DerivedB
。强制转换将在运行时失败。
更新
在阅读了您的一些评论后,以下是我将如何设计它:
public interface INonGen
{
object Value { get; }
}
public interface IGen<U> : INonGen
{
}
public class Gen<U> : IGen<U>
{
private U u;
public object Value
{
get { return u; }
}
}
从字典中提取项目时:
double value = (double)dictionary[key].Value;
如果没有运行时转换,将引发InvalidCastException
。很简单,不是吗?
我不确定在这种情况下INonGen
的目的是什么,特别是因为它有一个通用方法。如果你摆脱了它,你可以做到这一点。哪个编译 - 我检查了;o)
public interface IGen<T>
{
void Get(ref T value);
}
public class Gen<T, U> : IGen<T> where U : T
{
private U u;
public void Get(ref T value)
{
if (value is U)
{
value = (T)u;
}
else
throw new Exception();
}
}
关键是不能仅在接口方法上使用泛型类型参数,因为这会阻止您在实现类中指定U : T
约束。它必须在接口定义本身上。并且实现类必须明确地知道泛型类型参数U
和T
,以便编译器可以验证强制转换操作。
你可以沿着使用as
的路线走下去,但这并不安全,所以你必须处理结果null
的情况,而不是依赖编译器来做到这一点。不推荐。
如果在实际示例中,INonGen
有其他非泛型方法,或者泛型方法,其中实现类不需要知道实现方法之外的方法的泛型类型参数,那么您可以毫无问题地恢复它。
public interface INonGen
{
void NonGenericMethod();
void GenericMethod<V>(V parameter);
}
public interface IGen<T> : INonGen
{
void Get(ref T value);
}
public class Gen<T, U> : IGen<T> where U : T
{
private U u;
public void Get(ref T value)
{
if (value is U)
{
value = (T)u;
}
else
throw new Exception();
}
public void NonGenericMethod()
{
}
public void GenericMethod<V>(V parameter)
{
}
}
您需要使用概念调用约束...
public class Gen<U> : IGen<U> where U : T
{
private U u;
public void Get<T>(ref T value)
{
if (value is U)
{
value = (T) u;
}
else
throw new Exception();
}
}
这告诉您U
必须是或派生自为T
提供的参数。
首先,也不要使用ref
。然后你的问题就变得没有意义了。
public interface IThing
{
object Value { get; }
}
public interface IThing<T> : IThing
{
T Value { get; }
}
public class Thing<T> : IThing<T>
{
private T t;
public object Value
{
get
{
return this.Get();
}
}
public T Value<T>()
{
get
{
return this.t;
}
}
}
如果你真的想接受一些通用的其他,那就被限制在正确的类型上。
public interface ICrazyThing<T>
{
void Get<T>(ref T crazy);
}
public class CrazyThing<U, T> : IThing<T> where T : U
{
private U u;
public void Get<T>(ref T crazy)
{
crazy = this.u;
}
}
即使在疯狂的世界里,out
也是比ref
更好的选择,因为传入的值是一个毫无意义的实例化,与结果无关。