【问题标题】:Chaining implicit operators in generic c# classes在泛型 c# 类中链接隐式运算符
【发布时间】:2011-05-14 12:33:50
【问题描述】:

对于以下通用 c# 类,我想将 T 转换为 K:

public abstract class ValueType<T,K> : IValueType<T> where K : ValueType<T,K>,new()
{     
    public abstract T Value { get; set; }     

    public static implicit operator ValueType<T,K>(T val) 
    {         
        K k = new K();
        k.Value = val;
        return k;    
    }
}

如果我要实现直接运算符implicit operator K(T val),它将导致编译时错误 (CS0556)。

我想我可以尝试链接隐式运算符:

public static implicit operator K(ValueType<T,K> vt){
    return (K)val;
}

但是下面的例子还是报错不能转换:

public class Test : ValueType<int, Test>
{
    public override int Value{ get; set; }
}

Test t = 6; //complains it's unable to be converted
Console.WriteLine(t.Value);

如果可能的话,我真的想避免显式转换。

这个问题延伸到我之前提出的another SO question

【问题讨论】:

  • 我不认为编译器确实将隐式链接应用于强制转换/转换运算符......所以我看不到这里的重点。还是我错过了什么?
  • @Lucero 我想知道是否可以链式转换/转换。如果没有,是否有解决方法?

标签: c# operator-overloading abstract-class


【解决方案1】:

实现您自己的隐式转换逻辑的规则非常严格,如果您要执行此类特别复杂的操作,您可能应该非常熟悉规范的 6.4.4 和 10.10.3 部分。

简而言之,您应该知道的几个关键规则是:

  • 定义转换的类型必须出现在用户定义转换的“to”或“from”部分。您不能创建定义从 E 到 F 的转换的 C 类; C 必须在某个地方。
  • 永远不可能用您自己的隐式转换替换内置的隐式转换;例如,在从 C 转换为对象时,您不能发生特殊行为。
  • 用户定义的隐式转换将与最多两个内置隐式转换“链接”,但不会与任何其他用户定义的转换“链接”。例如,如果您有一个用户定义的从 C 到 D 的隐式转换,以及一个从 D 到 IFoo 的内置隐式转换,那么您将获得从 C 到 IFoo 的隐式转换。但是,如果 D 具有用户定义的到 E 的隐式转换,那么您不会免费获得从 C 到 E 的隐式转换。

【讨论】:

  • 如果定义了来自 Func 的隐式转换 -> MyClass&lt;T&gt; 的隐式转换,这似乎适用于 lamba -> Func -> MyClass&lt;T&gt;,但编译器实际上似乎并不链接转换。
  • @NetMage:您能否通过引用规范来证明您的说法“应该有效”?规范的这个领域是微妙的;仔细阅读。
  • @NetMage:或者,从编译器开发人员的角度考虑。赋值右侧有一个 lambda,左侧有一个 MyClass&lt;T&gt; 类型的变量。 MyClass&lt;T&gt; 具有任意多个委托类型的任意多个转换。描述你认为类型推断应该如何从这些信息中推导出 lambda 的形式参数类型。我想如果你这样做了,你就会明白为什么我没有那样做。
  • 我想我发现了我的错误,即只有标准的隐式转换与用户定义的转换链接在一起,并且标准隐式转换(6.3.1)的列表不包括匿名函数转换(6.5 )。根据您的问题,现有的匿名函数转换能否提供一组兼容的委托类型,可用于创建一组可能的用户定义转换,然后应用唯一规则?
  • @NetMage:正确;匿名函数转换不是“标准的”。现在,你可能会说,让我们解决这个问题。我们将其视为任何其他重载解决问题; lambda 到委托的转换用于确定运算符的第一个适用性,然后是更好的。源表达式需要被视为没有类型,这使特异性规则有些复杂,因为它们是根据具有类型的表达式编写的。
【解决方案2】:

编译器不会链接转换,因此解决问题的方法不起作用。

隐式转换在类型检查中非常严格。如果编译器知道类型,你的第一个 sn-p 和 Testdo 工作:

ValueType<int, Test> t = 6;
Console.WriteLine(t.Value);

问题是您的ValueType&lt;int, Test&gt; - 从类型系统的角度来看 - 并不总是Test,因此隐式转换不适用于那里。

顺便说一下,Eric Lippert 写了一篇 blog post 关于这种通用自引用的文章 - 值得一读!

【讨论】:

    【解决方案3】:

    据我所知,我不认为你可以将演员连在一起,对此感到抱歉。

    我一直在研究如何创建解析器,如果可以的话,必须有一个无限循环才能找到从TK 的连接。我不确定 C# 解析器会尝试这样做,但不幸的是,我的钱没有!

    【讨论】:

    • 如果您将类型系统视为类型图,通过不重新访问您在此尝试中先前访问过的任何类型(节点),很容易避免循环上的无限循环解析类型。所以这不是原因...... ;)
    • 有趣的 Lucero,您认为您会使用访问者模式还是在某种地图中记录您访问过的类型?
    • 我不喜欢或不使用访问者模式,因为它breaks the fundamental open/closed principle。也就是说,最简单的方法是将一组已访问节点作为参数传递并对其进行检查,或者执行广度优先搜索方法,将待处理的节点添加到队列中,如果它们丢弃它们当您将它们出列时,它们已经存在于集合中。
    • 我对 JavaCC 的唯一体验,它是内置的访问者支持,我也不太喜欢它!我同意你的第二个想法,它也更容易放弃更改,就像访问者模式一样,你必须返回并更改你设置的 bool
    • 基本上归结为将事物视为对象图,而不仅仅是树。树从来没有循环,图通常有,因此在这种情况下,寻找处理图的算法是要走的路。顺便说一句,如果你想在 .NET 中做一些解析,你可能想看看 this - (哦,免责声明,我是那篇文章中使用的 perser 引擎的作者,所以我确实有一些偏见朝向这个解析器引擎;))。
    猜你喜欢
    • 1970-01-01
    • 2015-12-07
    • 1970-01-01
    • 2012-04-25
    • 1970-01-01
    • 2010-10-21
    • 1970-01-01
    • 2011-08-19
    相关资源
    最近更新 更多