4

我来自 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

4 回答 4

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如果没有运行时转换,将抛出一个。很简单,不是吗?

于 2013-10-17T11:19:01.123 回答
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)
    {
    }
}
于 2013-10-17T11:40:19.257 回答
0

您需要使用概念调用约束...

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

于 2013-10-17T11:15:16.317 回答
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,因为传入的值是与结果无关的毫无意义的实例化。

于 2013-10-17T11:25:55.710 回答