我有一个从string
隐式转换的类,定义为:
class TestClass : ITestInterface
{
public static implicit operator TestClass(string value)
{
return new TestClass(value);
}
public TestClass(string value)
{
Value = value;
}
public string Value { get; set; }
}
它实现了标记器接口:
public interface ITestInterface { }
我有另一个类的方法定义为:
public void DoSomething(ITestInterface thing) { }
尝试调用该方法时出现错误:
public void Test()
{
TestClass a = "This works fine.";
DoSomething("Why doesn't this work?");
}
无法从“字符串”转换为“ Tests.ITestInterface”
所有代码都大大简化了;我的实际要求要复杂得多,但这似乎是阻止我要实现的模式的核心。
是什么阻止了此工作? (C#规范中有什么?)
我可以对代码进行任何更改以允许这种类型的转换正常工作吗?
答案 0 :(得分:5)
您将省略解释该问题的第三个选项:
//1
TestClass a = "This works fine.";
//2
ITestInterface i = "This doesn't work either!";
//3
DoSomething("Why doesn't this work?");
在(1)中,您已声明TestClass a
。这意味着编译器知道当您使用其他类型(在这种情况下为字符串)时,编译器应尝试将所述值转换为TestClass
。
在(2)中,您已声明ITestInterface i
。这意味着编译器知道当您使用其他类型(在这种情况下为字符串)时,编译器应尝试将所述值转换为ITestInterface
。
这是问题的根源。在string
和ITestInterface
之间没有定义转换。
您目前在想的是:
好吧,我知道我希望将其转换为
TestClass
。为什么编译器不知道我想要它做什么?
简短的回答是编译器拒绝猜测。
您想要发生的事情会导致无法实现的情况。例如,如果第二个类也实现ITestInterface
会发生什么?
class SecondTestClass: ITestInterface
{
public static implicit operator SecondTestClass(string url)
{
return new SecondTestClass(url);
}
public SecondTestClass(string url)
{
Value = GetValueFromTheInternet(url);
}
public string Value { get; set; }
}
让我们重新评估您的代码:
//1
TestClass a = "This works fine.";
这有效。从string
到TestClass
的转换。
//2
SecondTestClass b = "This works fine.";
这有效。从string
到SecondTestClass
的转换。
//3
ITestInterface i = "This still doesn't work!";
//4
DoSomething("This doesn't work for the same reason as //3");
这不起作用。编译器没有从string
到ITestInterface
的任何已知转换。
编译器无法确定是要将其转换为TestClass
然后分配给i
,还是要将其转换为SecondTestClass
和然后分配给i
。
而且,正如我之前所说,编译器拒绝猜测。
同样,为了清楚起见,这将起作用:
TestClass a = "This works fine.";
ITestInterface i1 = a;
DoSomething(a);
DoSomething(i1);
SecondTestClass b = "This works fine.";
ITestInterface i2 = b;
DoSomething(b);
DoSomething(i2);
所有这些分配工作。
问题的症结在于,编译器希望您明确地声明要将字符串转换为哪种类型。在您自己的示例中,您已经明确要求使用TestClass
。请注意,如果您使用var
,这将不起作用,因为在这种情况下,编译器也无法弄清楚。
答案 1 :(得分:3)
从编译器错误中非常清楚,不是吗? DoSomething
期望ITestInterface
的实例,string
不会实现。从字符串到您的类的隐式转换这一事实并不意味着此转换也适用于实现该接口的任何其他类。
想象还有另一个实现该接口的类:
class AnotherTestClass : ITestInterface { }
现在如何解决DoSomething
呼叫?转换适用于哪一类?是TestClass
的实例还是AnotherTestClass
的实例?特别是如果AnotherClass
也定义了隐式强制转换运算符。这就是为什么它不起作用的原因。
或者反过来考虑:当您只有接口但根本没有实现它的类时(在设计API时很常见),根本就没有转换。您的设计将一些静态绑定从接口引入到接口的具体实现中,这是一件坏事。实际上,这使您的DoSomething
方法仅适用于TestClass
类型的实例,这与使用接口作为参数是矛盾的。因此,使用API只能使用向方法提供TestClass
的实例。
除此之外,我怀疑演员在这里是好事。通过隐式转换,您意味着每个字符串都可以安全地转换为您的类,而不会丢失任何信息。例如。 URI是您的类的有效表示吗?甚至您提供的"Why doesn't this work?"
?
另一方面,期望一个字符串的构造函数要精确得多,而且要清楚:
var m = new TestClass(myString);
根据我的经验,只有极少数情况下确实需要隐式强制转换。您做得更多的是根据一些输入创建一些实例,并向该实例追加更多数据。在您的示例中,这意味着TestClass
包含一些字符串信息,但可能还有更多数据。
答案 2 :(得分:0)
public void DoSomething(ITestInterface thing) { }
因为该参数是接口,并且您想调用static implicit operator TestClass(string value)
,但这是不可能的。 C#中的接口不能使用static
方法。
您只能将类TestClass
作为参数传递
public static void DoSomething(TestClass thing) { Console.WriteLine(thing.Value); }