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 进行访问;也就是说,我不希望在界面中显式使用类型集。

C# 和强制转换泛型参数

将一个对象强制转换为另一个对象时,如果编译器找不到转换,则会报告错误。由于这两个类型参数不受约束,因此唯一的选择是使用 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约束。它必须在接口定义本身上。并且实现类必须明确地知道泛型类型参数UT,以便编译器可以验证强制转换操作。

你可以沿着使用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更好的选择,因为传入的值是一个毫无意义的实例化,与结果无关。