在不可变的接口上暴露可变接口的最佳方法是什么?

时间:2011-05-04 16:08:39

标签: c# inheritance interface properties readonly

我想知道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。

2 个答案:

答案 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,但反之则不然。