在我正在开发的库中,我需要多个不相关的类型来为(可能很多)“标签”提供值。每个标记都有一个相关的值类型,并表示为继承以下通用基础的(单例)类:
public abstract class Tag<TTag,TValue>
where TTag: Tag<TTag,TValue>
{
}
(类型参数TTag
采用了奇怪的重复模板模式。)
我想提供一个接口IProvider<TTag>
,它可以标记可以为特定标记提供值的类。理想情况下,界面看起来像这样:
public interface IProvider<TTag>
{
TValue GetValue<TValue>(Tag<TTag,TValue> tag);
}
这将允许从提供的标记中推导出值的类型
(如果类型是多个标签的提供者)。
不幸的是,使用类Tag<TTag,TValue>
需要他约束
TTag: Tag<TTag,TValue>
,无法在此方法中指定,因为它唯一的通用参数是TValue
。我尝试这样做:
public interface IProvider<TTag>
{
TValue GetValue<TTag2,TValue>(Tag<TTag2,TValue> tag)
where TTag2: Tag<TTag2,TValue>, TTag;
}
我的意图是,如果TTag
是继承Tag<TTag,TValue>
的标记类,
TTag2
满足这些约束的唯一方法是等于TTag
。
以这种方式编写的接口可以正确编译。但是,实施会导致问题:
public struct Data<TTag,TValue> : IProvider<TTag>
where TTag: Tag<TTag,TValue>
{
public TValue Value;
public TValue2 GetValue<TTag2,TValue2>(Tag<TTag2,TValue2> tag)
where TTag2: Tag<TTag2,TValue2>, TTag
{
return (TValue2) Value;
}
}
以上无法使用错误CS0455进行编译,因为类型TTag2
继承了两个不相关的类约束Tag<TTag2,TValue2>
和Tag<TTag,TValue>
(语言规则没有注意到这些“明显”的事实必须是平等的)。使该方法显式实现接口...崩溃编译器(mono 3.2.8)。
经过这么长时间的介绍,我的问题就出现了:是否有可能提出IProvider<TTag>
的可实现定义而不牺牲任何(希望直觉上明确的)约束条件?如果没有,您能为原始方案建议不同的类型层次结构吗?谢谢!
P.S。让我排除任何将标记的值类型作为IProvider
的泛型参数提及的实现 - 实际值类型足够复杂,因此无法推断它们会完全杀死使用库。
答案 0 :(得分:0)
如果我看到这个结构快速浏览,那么在Data
结构中,您要说的是TValue Value
字段TValue2
作为GetValue
返回public abstract class Tag<TTag, TValue>
where TTag: Tag<TTag,TValue>
{
}
public interface IProvider<TTag, TValue>
where TTag: Tag<TTag,TValue>
{
TValue GetValue(TTag tag);
}
public struct Data<TTag, TValue> : IProvider<TTag, TValue>
where TTag: Tag<TTag, TValue>
{
public TValue Value;
public TValue GetValue(TTag tag)
{
return Value;
}
}
方法。它的类型不一样。
所以我能想出的唯一能做到这一点的是:
public interface IProvider<TTag>
{
TValue GetValue<T, TValue>(T tag) where T : Tag<T, TValue>;
}
public struct Data<TTag, TValue> : IProvider<TTag>
{
public TValue Value;
public TV GetValue<T, TV>(T tag) where T : Tag<T, TV>
{
return (TV)(object)Value;
}
}
但这与您在问题中的要求相冲突。
这让你更接近:
return (TV)(object)Value;
但问题再次出现在TValue
行 - 您无法将TV
的值转换为GetValue
,因为此代码的重点是留下回报TValue
方法的类型免费。但这是不可能的。
您必须在IProvider
界面上拥有{{1}}参数。