当“相同”时不会出现编译错误。用户定义的转换存在两次

发布于 2024-10-19 06:21:00 字数 1255 浏览 4 评论 0原文

在 C# 中,为什么当“相同”的用户定义转换存在两次时没有编译错误? (一次在源类中,一次在目标类中?)

例如,如果我尝试编译以下代码,则不会出现编译错误:

namespace TestConversionOverloading
{
    public class A
    {
        public int value = 1;

        public static explicit operator B(A a)
        {
            B b = new B();

            b.value = a.value + 6;

            return b;
        }
    }

    public class B
    {
        public int value = 2;

        public static explicit operator B(A a)
        {
            B newB = new B();

            newB.value = a.value;

            return newB;
        }
    }

    public class program
    {
        public static void Main() {}
    }
}

但是,如果我尝试将 A 显式转换为 B,我会这样做 出现编译错误。假设我将以下内容添加到 Main() 并尝试编译:

A a = new A();
B b = ((B)a);

我将得到以下内容:

用户定义的转化不明确 'TestConversionOverloading.A.显式运算符 TestConversionOverloading.B(TestConversionOverloading.A)'
'TestConversionOverloading.B.explicit 操作员 TestConversionOverloading.B(TestConversionOverloading.A)'
“TestConversionOverloading.A”转换为“TestConversionOverloading.B”时

那么为什么不直接从定义中给出错误呢?有没有办法使用这两种转换?

In C#, why are there no compilation errors when the "same" user-defined conversion exists twice? (once in the source class and once in the target class?)

For example, if I try to compile the following code I get no compilation errors:

namespace TestConversionOverloading
{
    public class A
    {
        public int value = 1;

        public static explicit operator B(A a)
        {
            B b = new B();

            b.value = a.value + 6;

            return b;
        }
    }

    public class B
    {
        public int value = 2;

        public static explicit operator B(A a)
        {
            B newB = new B();

            newB.value = a.value;

            return newB;
        }
    }

    public class program
    {
        public static void Main() {}
    }
}

However, if I try to explicitly convert A to B, I do get a compilation error. Say I add the following to Main() and try to compile:

A a = new A();
B b = ((B)a);

I'll get the following:

Ambiguous user defined conversions
'TestConversionOverloading.A.explicit operator TestConversionOverloading.B(TestConversionOverloading.A)'
and
'TestConversionOverloading.B.explicit
operator
TestConversionOverloading.B(TestConversionOverloading.A)'
when converting from 'TestConversionOverloading.A' to 'TestConversionOverloading.B'

So why not give an error straight from definition? Could there be a way to use either conversion?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

心的位置 2024-10-26 06:21:00

我不会推测为什么语言允许这样做是有意义的,但如果您控制这两个类,那么明显的解决方案是摆脱其中一个运算符。

如果你不能,这里有一种使用反射来消除歧义的方法。

首先,创建一个绑定到预期运算符的委托:

// Picks the conversion operator declared in class A.
var method = typeof(A).GetMethod("op_Explicit", new[] { typeof(A) });
var converter = (Func<A, B>)Delegate.CreateDelegate(typeof(Func<A, B>), method);

然后将该委托用作:

A a = ...
B b = converter(a);

I wouldn't speculate on why it makes sense for the language to allow this, but if you are in control of both classes, the obvious solution is to get rid of one of the operators.

If you can't, here's a way to disambiguate using reflection.

First, create a delegate that binds to the intended operator:

// Picks the conversion operator declared in class A.
var method = typeof(A).GetMethod("op_Explicit", new[] { typeof(A) });
var converter = (Func<A, B>)Delegate.CreateDelegate(typeof(Func<A, B>), method);

And then use the delegate as:

A a = ...
B b = converter(a);
快乐很简单 2024-10-26 06:21:00

根据规范,这是预期的行为。

大量压缩原始文本,在这种情况下会发生以下情况:编译器将在两个类定义中找到所有可以将 A 转换为 B 的运算符。这将招募 A 运算符 B(A a)B 运算符 B(A a)。然后,

如果不存在这样的运算符,或者存在多个这样的运算符,则转换不明确并且会发生编译时错误。

那么为什么不直接从定义中给出错误呢?因为这两个定义都可以,但正是它们的使用导致了问题的出现。

有没有办法使用这两种转换?我没有看到一个简单的方法来做到这一点。我正在考虑绕过编译器,手动发出 IL。这样我认为您可以指示程序使用一个运算符或另一个运算符。但不确定这是否完全可行。像 Reflector 这样的工具可以提供帮助。

虽然使用基于运算符的转换有一些优点,但其中一个类将丢失一个运算符,或者您可以更改为基于构造函数的转换或更简单的语法 ToA(A a)FromA(A a)。或者也许埃里克·利珀特(Eric Lippert)可以用一些语言智慧来启发我们!

According to the spec, this is the expected behavior.

Heavily compressing the original text, here's what happens in this case: the compiler will find all operators that could convert A to B in both class definitions. This would enlist A operator B(A a) and B operator B(A a). Then,

If no such operator exists, or if more than one such operator exists, then the conversion is ambiguous and a compile-time error occurs.

So why not give an error straight from definition? Because both definitions are OK, but it's their use that makes the problem arise.

Could there be a way to use either conversion? I don't see an easy way to do this. I'm thinking of bypassing the compiler, emitting IL by hand. That way I think you can instruct the program to use one operator or the other. Not sure if this is entirely feasible, though. A tool like Reflector could help.

While there's some beauty using operator-based conversions, either one of the classes will lose one operator, or you could change to constructor-based conversions or a more straightforward syntax of ToA(A a) and FromA(A a). Or maybe Eric Lippert could enlighten us with some language cleverness!

紙鸢 2024-10-26 06:21:00

看看每个“public static 隐式运算符 B(A a)”代码行生成的 IL 代码:

.method public hidebysig specialname static
class TestConversionOverloading.B  op_Explicit(class TestConversionOverloading.A a) cil managed

所以这是第一个问题的答案:隐式/显式转换运算符是语法糖。在 MSIL 中,它们看起来像常用方法(而且确实如此)。当两个不同的类具有具有相同签名的方法时,不存在任何犯罪行为,因为它不违反任何规定。尽管在这种情况下无法编译转换运算符调用。
正如前面提到的,您可以使用反射来获取任一方法的 MethodInfo。

Look at the IL code generated per "public static implicit operator B(A a)" code line:

.method public hidebysig specialname static
class TestConversionOverloading.B  op_Explicit(class TestConversionOverloading.A a) cil managed

So here is the answer on the first question: Implicit/explicit convertion operators are syntactic sugar. In MSIL they are look like usual methods (and they are). There is nothing criminal when two different classes have methods with identical signature as it does not violate anything. Though conversion operator call cannot be compiled in this case.
And as it was mentioned you can use reflection to get MethodInfo of either method.

陪你搞怪i 2024-10-26 06:21:00

请记住,冲突的转换之一可能是通用的,并且对于通用参数的其他组合可能有用。

您甚至可以在同一个类中定义冲突的转换:

class C<T>
{
    implicit operator int() { return 0; }
    implicit operator T() { return default(T); }
}

C<int> c;
int i = c;

如果编译器抱怨这一点,您将失去 C 转换为 stringint

Remember that one of the conflicting conversions could be generic, and could be useful for other combinations of generic parameters.

You can even have conflicting conversions defined in the SAME class:

class C<T>
{
    implicit operator int() { return 0; }
    implicit operator T() { return default(T); }
}

C<int> c;
int i = c;

If the compiler complained about this, you'd lose the ability for C<string> to convert to both string and int.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文