C#和转换泛型参数

时间:2013-10-17 11:12:28

标签: c# generics types casting

我来自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进行访问;也就是说,我不希望在接口中明确显示这组类型。

4 个答案:

答案 0 :(得分:3)

将对象转换为另一个对象时,如果编译器无法找到转换,则会报告错误。由于两个类型参数不受约束,唯一的选择是使用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。简单,没有?

答案 1 :(得分:1)

我不确定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)
    {
    }
}

答案 2 :(得分:0)

您需要使用概念调用constraints ...

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提供的参数派生。

答案 3 :(得分:0)

首先,不要使用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更好的选择,因为传入的值是无意义的实例化,与结果无关。