【问题标题】:Implicit conversions not considered in overload resolution?重载决议中不考虑隐式转换?
【发布时间】:2015-01-15 14:18:23
【问题描述】:

我正在尝试包装一个类型(在我的控制之外),以便它看起来无缝地实现了一个接口(也在我的控制之外)。

鉴于这些定义

// 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 的转换,并且出现“无法转换”编译器错误。如果我手动添加演员表GetSignature((FooSignaturizer) foo),它会起作用。但是,我还需要添加对BarQux 类型的支持,包括BarSignaturizerQuxSignaturizer,因此演员表不适用于这些情况。

有没有办法做到这一点?

【问题讨论】:

  • 为什么FooSignaturizer 是一个结构而不是一个类?
  • 您是否尝试过创建对接口类型的隐式强制转换?
  • @OmriAharon:如果我让它工作,它将用于代码库中性能非常关键的部分,分配非常多。有人建议,可能会在堆栈上分配一个结构,以减少垃圾收集器的负载。这在一定程度上是一个测试,看看这在“现实世界”的案例中是否真的如此。
  • @juharr:我认为你不能对接口进行隐式转换。
  • @juharr:据我所知这是不可能的?如果我错了,请赐教:)

标签: c# implicit-conversion overload-resolution


【解决方案1】:

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

7.5.3.1 适用函数成员

当满足以下所有条件时,就参数列表 A 而言,函数成员被称为 适用的函数成员

  • A 中的每个参数对应于第 7.5.1.1 节中所述的函数成员声明中的一个参数,任何没有参数对应的参数都是可选参数。
  • 对于A中的每个实参,实参的传参方式(即value、refout)与对应参数的传参方式相同,且
    • 对于值形参或形参数组,存在从实参到相应形参类型的隐式转换(第 6.1 节),或者
    • 对于refout 参数,实参的类型与对应参数的类型相同。毕竟,refout 参数是所传递参数的别名。

这里的不是从FooIGenereateSignature 的隐式转换,而是一个包装器。

作为对这种行为的解释,您不能期望编译器在范围内检查IGenerateSignature 的每个实现,以查看它是否具有与Foo 的隐式转换。如果有多个呢?


关于如何为FooBarQux 实现这一目标...

您想要实现的目标是调用GetSignature(fooOrBarOrQux) 是不可能的,因为(根据您对Foo 的描述)您不能有一个变量可以是Foo Bar Qux 在编译时 - 它们不相关。您将始终需要三个调用站点,因此没有理由不针对这三种情况进行三个略有不同的转换(包装类或重载方法调用等)。

...除非你使用dynamic?

【讨论】:

  • 我实际上在发帖之前尝试阅读该段,但无法真正理解它:(但你的解释很好,谢谢:)
  • @carlpett 是的,规范在某些地方可能会变得很繁重,但只要你有空闲时间,它确实是一本有趣的书。
【解决方案2】:

Rawling 的回答很好地解释了您遇到问题的原因。由于您无法通过隐式转换来解决此问题,您可以尝试使用扩展方法将所有类型转换为 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);
    }
    //....

}

【讨论】:

    【解决方案3】:

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

    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());
    

    这不是很优雅,但我认为应该能满足你的需要。

    【讨论】:

      猜你喜欢
      • 2016-05-25
      • 2014-03-25
      • 1970-01-01
      • 1970-01-01
      • 2015-03-29
      • 1970-01-01
      • 2017-08-04
      • 1970-01-01
      • 2021-12-10
      相关资源
      最近更新 更多