首页 > 解决方案 > 从方法签名中的父类推断泛型失败

问题描述

我正在使用一个库,其中包含(数百个模板生成的)没有泛型修饰符的类,每个类都使用泛型修饰符扩展相同的类(本质上是为了缩短符号)。例如

class DArr : NumberObject<double, MyArrayIndexer> { ... }
class IArr : NumberObject<int, MyArrayIndexer> { ... }
class DMat : NumberObject<double, MyMatrixIndexer> { ... }
class IMat : NumberObject<int, MyMatrixIndexer> { ... }
class BMat : NumberObject<bool, MyMatrixIndexer> { ... }
and so on

我现在想编写处理这些函数的函数,该函数需要它们的内部类型(例如,内部副本需要知道每个元素是 4 字节还是 8 字节,以及正在使用什么类型的索引器)。因此,我对我的功能进行了签名:

public SomeUnrelatedClass<T> Process<T,TVal,T0>(SomeUnrelatedClass<T> obj) where T : NumberObject<TVal,T0> {
 //here some tstuff that requires T, TVal and T0
} 

但不幸的是,我似乎无法以我需要的方式使用它,即

SomeUnrelatedClass<DArr> input = ...;
SomeUnrelatedClass<DArr> output = Process(input);

因为它无法从(为什么??)推导出 TVal 和 T0,尽管它是唯一确定的。我该如何补救?将其称为SomeUnrelatedClass<DArr> output = Process<DArr,double,MyArrayIndexer>(input);不是一种选择,因为实际上这些泛型有很多而且它们的名称也更长(而且这个函数将在未来的代码中使用数千次,所以我宁愿现在写一个更多复杂的函数,它正确并保持语法简单)。理想情况下,我不会做一长串“if type == ...”,因为类型列表是模板生成的,并且随着类型的添加会随着时间的推移而变化(这意味着这个模板将依赖于另一个模板, ETC。)

我想到了一些愚蠢的技巧,比如制作语法Process<T,TVal,T0>(SomeUnrelatedClass<T> obj,SomeUnrelatedClass<NumberObject<TVal,T0>> sameObj)和调用SomeUnrelatedClass<DArr> output = Process(input,input);,但我觉得这只是一个非常糟糕的修复。有什么更“正确”的方法来做到这一点?

标签: c#genericsinheritance

解决方案


我认为您在这里可能会很幸运,因为 C# 不会根据您指定的约束来推断类型。我认为这是规范的一部分,我可以挖掘出来。但无论如何你也许可以解决你的问题:) - 我设法让以下运行。

{
    var input = new DArr(12.34);
    var output = Visitor.Process(input);
    Console.WriteLine($"Type [{output.Obj.GetType()}] with value {output.Obj.MyType}");
}
{
    var input = new IArr(42);
    var output = Visitor.Process(input);
    Console.WriteLine($"Type [{output.Obj.GetType()}] with value {output.Obj.MyType}");
}

// Generated output:
//
// Type [DArr] with value 12,34
// Type [IArr] with value 42

因此,无论您定义了多少类型,Process方法都会使用并将输入映射到正确的具体类型。

以下是它的工作原理:

public class NumberObject<TType, TIndexer>
{
    public NumberObject(TType type) { MyType = type; }
    public TType MyType { get; }
}

public class MyArrayIndexer { }

public class DArr : NumberObject<double, MyArrayIndexer>
{
    public DArr(double value) : base(value) { }
}

public class IArr : NumberObject<int, MyArrayIndexer>
{
    public IArr(int value) : base(value) { }
}

public class SomeUnrelatedClass<T>
{
    public T Obj { get; }

    public SomeUnrelatedClass(T obj){ Obj = obj; }
}

public class Visitor
{
    public static SomeUnrelatedClass<NumberObject<TVal, T0>> Process<TVal, T0>(NumberObject<TVal, T0> input)
    {
        return new SomeUnrelatedClass<NumberObject<TVal, T0>>(input);
    }
}

这里的技巧是减少泛型参数的数量 - 摆脱T类型,因为它可以直接由其余两个表示,因为您实际上也自己编写

T is NumberObject<TVal, T0>

当要推断的类型级别降低时,C# 编译器可以直接确定它们,中提琴您可以让您为Process方法指定泛型参数。


推荐阅读