重载决议中不考虑隐式转换?
Implicit conversions not considered in 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;
}
}
但由于某些原因,重载解析无法找到从 Foo
到 FooSignaturizer
的转换,我得到一个 "Cannot convert" 编译器错误。如果我手动添加演员,GetSignature((FooSignaturizer) foo)
,它会起作用。但是,我还需要添加对 Bar
和 Qux
类型以及 BarSignaturizer
和 QuxSignaturizer
类型的支持,因此转换不适用于这些情况。
有没有办法做到这一点?
根据 C# 规范的 7.5.3.1,仅考虑从参数表达式到参数类型的隐式转换。
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
, or out
) 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
or out
parameter, the type of the argument is identical to the type of the corresponding parameter. After all, a ref
or out
parameter is an alias for the argument passed.
这里不是从 Foo
到 IGenereateSignature
的隐式转换,它是一个包装器。
作为对此行为的解释,您不能要求编译器遍历范围内 IGenerateSignature
的每个实现以查看它是否具有隐式转换 to/from Foo
.如果不止一个呢?
关于如何为 Foo
、Bar
和 Qux
实现此目标...
您想要实现的目标是调用 GetSignature(fooOrBarOrQux)
,这是不可能的,因为(根据您对 Foo
的描述)您不能有一个变量可以a Foo
or a Bar
or a Qux
在编译时 - 它们是不相关的。您将始终需要三个调用站点,因此没有理由不对这三种情况进行三种略有不同的转换(包装器 class 或重载方法调用或其他)。
...除非你使用 dynamic
?
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;
}
}
现在BarSignaturizer
和QuxSignaturizer
可以实现相同的接口。然后你可以这样做:
GetSignature(((ISignaturizer)fooOrBarOrQux).ToSignaturizer());
这不是很优雅,但是我认为应该可以满足您的需求。
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);
}
//....
}
我正在尝试包装一个类型(在我的控制之外),以便它看起来无缝地实现了一个接口(也在我的控制之外)。
给出这些定义
// 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
的转换,我得到一个 "Cannot convert" 编译器错误。如果我手动添加演员,GetSignature((FooSignaturizer) foo)
,它会起作用。但是,我还需要添加对 Bar
和 Qux
类型以及 BarSignaturizer
和 QuxSignaturizer
类型的支持,因此转换不适用于这些情况。
有没有办法做到这一点?
根据 C# 规范的 7.5.3.1,仅考虑从参数表达式到参数类型的隐式转换。
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.
这里不是从 Foo
到 IGenereateSignature
的隐式转换,它是一个包装器。
作为对此行为的解释,您不能要求编译器遍历范围内 IGenerateSignature
的每个实现以查看它是否具有隐式转换 to/from Foo
.如果不止一个呢?
关于如何为 Foo
、Bar
和 Qux
实现此目标...
您想要实现的目标是调用 GetSignature(fooOrBarOrQux)
,这是不可能的,因为(根据您对 Foo
的描述)您不能有一个变量可以a Foo
or a Bar
or a Qux
在编译时 - 它们是不相关的。您将始终需要三个调用站点,因此没有理由不对这三种情况进行三种略有不同的转换(包装器 class 或重载方法调用或其他)。
...除非你使用 dynamic
?
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;
}
}
现在BarSignaturizer
和QuxSignaturizer
可以实现相同的接口。然后你可以这样做:
GetSignature(((ISignaturizer)fooOrBarOrQux).ToSignaturizer());
这不是很优雅,但是我认为应该可以满足您的需求。
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);
}
//....
}