I'm trying to wrap a type (outside of my control) so that it would seamlessly appear to implement an interface (also outside of my control).
Given these defintions
// External types. Not changable.
class Foo {
public int I { get; set; }
public int J { get; set; }
}
interface IGenerateSignature {
string Generate();
}
I would like to use a Foo
instance to call a method with an IGenerateSignature
parameter:
void Test() {
var foo = new Foo { I = 1, J = 2 };
GetSignature(foo);
}
void GetSignature(IGenerateSignature sig) {
Console.Write(sig.Generate());
}
I tried creating an intermediary struct like this:
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;
}
}
But for some reason overload resolution fails to find the conversion from Foo
to FooSignaturizer
, and I get a "Cannot convert" compiler error. If I manually add a cast, GetSignature((FooSignaturizer) foo)
, it works. However, I need to also add support for the Bar
and Qux
types, with BarSignaturizer
and QuxSignaturizer
, so the cast won't work for those cases.
Is there a way to accomplish this?
As per 7.5.3.1 of the C# spec, only implicit conversions from argument expression to parameter type are considered.
7.5.3.1 Applicable function member
A function member is said to be an applicable function member with respect to an argument list A when all of the following are true:
- Each argument in
A
corresponds to a parameter in the function member declaration as described in §7.5.1.1, and any parameter to which no argument corresponds is an optional parameter.- For each argument in
A
, the parameter passing mode of the argument (i.e., value,ref
, orout
) is identical to the parameter passing mode of the corresponding parameter, and
- for a value parameter or a parameter array, an implicit conversion (§6.1) exists from the argument to the type of the corresponding parameter, or
- for a
ref
orout
parameter, the type of the argument is identical to the type of the corresponding parameter. After all, aref
orout
parameter is an alias for the argument passed.
What you have here isn't an implicit conversion from Foo
to IGenereateSignature
, it's a wrapper.
As an explanation for this behaviour, you can't exect the compiler to go though every implementation of IGenerateSignature
in scope to see whether it has an implicit conversion to/from Foo
. What if there was more than one?
In terms of how you can achieve this for Foo
, Bar
and Qux
...
What you're trying to achieve, one call to GetSignature(fooOrBarOrQux)
, isn't possible, because (based on your description of Foo
) you can't have one variable that can be a Foo
or a Bar
or a Qux
at compile time - they're unrelated. You'll always need three call sites, so there's no reason not to have three slightly-different conversions (wrapper class or overloaded method call or something) for the three cases.
... unless you use dynamic
?