我想知道C#中关于可变/不可变接口的最佳实践是什么。
我喜欢仅针对接口而不是真实对象;删除依赖项并允许更容易的测试。
我还经常公开只读的接口,这会降低错误。 但是,当我需要更改对象实例上的内容时,这会在实际代码中产生问题。
这是我正在尝试做的事情
public interface ISomething
{
string Name { get; }
}
public interface IMutableSomething : ISomething
{
string Name { get; set; }
}
...
public class ConsumerClass
{
//Note that I'm working against the interface, not the implementation
public void DoSomethingOnName(ISomething o)
{
var mutableO = (IMutableSomething) o;
mutableO.Name = "blah";
}
}
以这种方式工作使我能够轻松测试ConsumerClass并破坏ISomething与其实现之间的任何依赖关系
我知道我可以将接口强制转换为实现,但这会引入对实际实现的依赖。
我可以做类似下面的事情,但我发现它很丑陋而烦人
public interface IMutableSomething : ISomething
{
void SetName(string newName)
}
or
public interface IMutableSomething // No inheritance, implementation impl. 2 interfaces
{
string Name { get; set; }
}
谢谢,
Eric G。
答案 0 :(得分:0)
这不是对接口的正确使用;界面是这样你不关心什么是实现,你只需使用定义的属性和方法。如果您需要“设置”具有get get接口的内容,则不应将该接口作为参数传递。
在这种情况下,如果必须使用接口,请在接口上定义set方法(或以不同方式实现属性)
public interface ISomething
{
string Name { get; set;}
void SetName(string newValue);
}
// Choose one of these methods to implement; both is overkill
public class SomethingElse : ISomething
{
protected string _internalThing = string.Empty;
public string Name
{
get { return _internalThing; }
set { throw new InvalidOperationException(); }
}
public void SetName(string newValue)
{
throw new InvalidOperationException();
}
}
然后简单地让不可变接口对值无效(或抛出异常)。
答案 1 :(得分:0)
我认为您的界面很好,但在您的消费者代码中,它应该如下所示:
public class ConsumerClass{
// Just take IMutableSomething
public void DoSomethingOnName(IMutableSomething o) {
o.Name = "blah";
}
}
方法调用是一个契约,正如其他人所说,你需要指定ConsumerClass
实际可以使用的最常规类型。您可能想要了解Liskov替换原则:http://en.wikipedia.org/wiki/Liskov_substitution_principle。
在这种情况下,虽然IMutableSomething可以替代ISomething,但反之则不然。