我试图包装一个类型(在我的控制之外),以便它可以无缝地显示实现一个接口(也在我的控制之外)。
鉴于这些定义
// 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;
}
}
但由于某种原因,重载解析无法找到从Foo
到FooSignaturizer
的转换,我得到了一个"无法转换"编译器错误。如果我手动添加演员GetSignature((FooSignaturizer) foo)
,它就可以了。但是,我还需要添加对Bar
和Qux
类型的支持,BarSignaturizer
和QuxSignaturizer
,因此广告素材不会适用于这些情况。< / p>
有没有办法实现这个目标?
答案 0 :(得分:4)
根据C#规范的7.5.3.1,只考虑从参数表达式到参数类型的隐式转换。
7.5.3.1适用的功能成员
当以下所有条件都成立时,关于参数列表A,函数成员被称为 适用的函数成员 :
A
中的每个参数对应于§7.5.1.1中描述的函数成员声明中的参数,并且任何无参数对应的参数都是可选参数。- 对于
A
中的每个参数,参数的参数传递模式(即值ref
或out
)与相应参数的参数传递模式相同,和
- 对于值参数或参数数组,从参数到相应参数的类型存在隐式转换(第6.1节),或
- 对于
ref
或out
参数,参数的类型与相应参数的类型相同。毕竟,ref
或out
参数是传递的参数的别名。
您在此处所拥有的不是从Foo
到IGenereateSignature
的隐式转换,它是一个包装器。
作为对此行为的解释,您无法让编译器通过范围内的IGenerateSignature
的每个实现来查看它是否具有到/ Foo
的隐式转换。如果有多个怎么办?
就Foo
,Bar
和Qux
......
您要尝试实现的目标,一次调用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;
}
}
现在BarSignaturizer
和QuxSignaturizer
可以实现相同的界面。然后你就可以做到:
GetSignature(((ISignaturizer)fooOrBarOrQux).ToSignaturizer());
这不是很优雅,但是我认为应该达到你所需要的。