在重载决策中不考虑隐式转换?

时间:2015-01-15 14:18:23

标签: c# implicit-conversion overload-resolution

我试图包装一个类型(在我的控制之外),以便它可以无缝地显示实现一个接口(也在我的控制之外)。

鉴于这些定义

// External types. Not changable.
class Foo {
    public int I { get; set; }
    public int J { get; set; }
}
interface IGenerateSignature {
    string Generate();
}

我想使用Foo实例来调用带有IGenerateSignature参数的方法:

void Test() {
    var foo = new Foo { I = 1, J = 2 };
    GetSignature(foo);
}

void GetSignature(IGenerateSignature sig) {
    Console.Write(sig.Generate());
}

我尝试创建这样的中间结构:

struct FooSignaturizer : IGenerateSignature {
    private readonly Foo _foo;
    public FooSignaturizer(Foo f) {
        _foo = f;
    }
    public static implicit operator FooSignaturizer(Foo f) {
        return new FooSignaturizer(f);
    }
    public string Generate() {
        return _foo.I + ":" + _foo.J;
    }
}

但由于某种原因,重载解析无法找到从FooFooSignaturizer的转换,我得到了一个&#34;无法转换&#34;编译器错误。如果我手动添加演员GetSignature((FooSignaturizer) foo),它就可以了。但是,我还需要添加对BarQux类型的支持,BarSignaturizerQuxSignaturizer,因此广告素材不会适用于这些情况。< / p>

有没有办法实现这个目标?

3 个答案:

答案 0 :(得分:4)

根据C#规范的7.5.3.1,只考虑从参数表达式到参数类型的隐式转换。

  

7.5.3.1适用的功能成员

     

当以下所有条件都成立时,关于参数列表A,函数成员被称为 适用的函数成员

     
      
  • A中的每个参数对应于§7.5.1.1中描述的函数成员声明中的参数,并且任何无参数对应的参数都是可选参数。
  •   
  • 对于A中的每个参数,参数的参数传递模式(即值refout)与相应参数的参数传递模式相同,和   
        
    • 对于值参数或参数数组,从参数到相应参数的类型存在隐式转换(第6.1节),或
    •   
    • 对于refout参数,参数的类型与相应参数的类型相同。毕竟,refout参数是传递的参数的别名。
    •   
  •   

您在此处所拥有的不是从FooIGenereateSignature的隐式转换,它是一个包装器。

作为对此行为的解释,您无法让编译器通过范围内的IGenerateSignature的每个实现来查看它是否具有到/ Foo的隐式转换。如果有多个怎么办?


FooBarQux ......

的实现方式而言

您要尝试实现的目标,一次调用GetSignature(fooOrBarOrQux)是不可能的,因为(根据您对Foo的描述),您不能拥有一个可以成为{的变量编译时{1}} Foo 一个Bar - 它们是无关的。你总是需要三个调用站点,因此对于这三种情况,没有理由不进行三次略微不同的转换(包装类或重载方法调用或其他)。

...除非您使用Qux

答案 1 :(得分:1)

罗林的回答很好地解释了为什么你遇到这个问题。由于您无法通过隐式转换修复此问题,因此您可以尝试使用扩展方法将所有类型转换为IGenerateSignature,如下所示:

void Test() {
    var foo = new Foo { I = 1, J = 2 };
    GetSignature(foo.AsIGenerateSignature());
}

void GetSignature(IGenerateSignature sig) {
    Console.Write(sig.Generate());
}

public static class GenerateSignatureExtensions
{
    public static IGenerateSignature AsIGenerateSignature(this IGenerateSignature me)
    {
        return me;
    }
    public static IGenerateSignature AsIGenerateSignature(this Foo me)
    {
        return new FooSignaturizer(me);
    }
    public static IGenerateSignature AsIGenerateSignature(this Bar me)
    {
        return new BarSignaturizer(me);
    }
    //....

}

答案 2 :(得分:0)

罗林的回答很好地解释了你遇到问题的原因。至于如何实现你想要的。我可能会考虑这样的事情:

public interface ISignaturizer
{
    IGenerateSignature ToSignaturizer();
}

struct FooSignaturizer : IGenerateSignature, ISignaturizer{
    private readonly Foo _foo;
    public FooSignaturizer(Foo f) {
        _foo = f;
    }

    public string Generate() {
        return _foo.I + ":" + _foo.J;
    }

    public IGenerateSignature ToSignaturizer()
    {
        return (IGenerateSignature)this;
    }
}

现在BarSignaturizerQuxSignaturizer可以实现相同的界面。然后你就可以做到:

GetSignature(((ISignaturizer)fooOrBarOrQux).ToSignaturizer());

这不是很优雅,但是我认为应该达到你所需要的。