【问题标题】:Will changing interface break existing clients更改界面是否会破坏现有客户端
【发布时间】:2016-09-02 15:05:55
【问题描述】:

前言:此代码用于 Windows 桌面应用程序、客户端/服务器应用程序,其中服务器使用基于 SMessage 的类向客户端发送和接收消息

我有如下界面

public interface IMessage
{
    string ID { get; }

    string R_ID { get; set; }

    DateTime Send { get; }
}

下面是这个接口的具体实现:

[Serializable]
public class SMessage : IMessage
{
    public string ID { get; set; }

    public string R_ID { get; set; }

    public DateTime Send{ get; set;}

    public SMessage()
    {
        R_ID = "";
        ID = Guid.NewGuid().ToString();
        Send = DateTime.UtcNow;
    }

    public SMessage(SMessage msg)
    {
        ID = msg.ID;
        Send = msg.UTCSend;
        R_ID = msg.R_ID;
    }

}

我已经使用上面的界面向世界发布了软件,现在我需要在这个界面“Where”中添加一条额外的数据

public interface IMessage
{
string ID { get; }

string R_ID { get; set; }

DateTime Send { get; }

string Where { get; }
}

我的问题:添加这条数据会破坏该领域的现有客户吗?

如果是这样,我如何更新接口/具体类以使现有客户端不会中断?

谢谢

附加信息:

SMessage 是在应用程序中发送的其他消息的基类:

public class InstallMessage : SMessage
{
}

public class ChangeState : SMessage
{
}

如何避免破坏现有客户?

所以,如果我这样做:

public interface IMessage2 : IMessage
{
string Where { get; }
}

还有这个:

public class SMessage : IMessage2
{
 // the correct implementation for IMessage2 is added and omitted here for brevity
}

所以我不确定的是如何处理我不知道消息是否来自 IMessage2 的情况? (注意:此代码在客户端和服务器应用程序中)

现场现有代码:

public void ReceiveChange( ChangeState msg )
{
    string x = msg.ID.ToString();
}

将在下一个版本中发布的新代码:

public void ReceiveChange( ChangeState msg )
{
    string x = msg.ID.ToString();

    // do I need to do some converting to keep from breaking ?
    IMessage2 iMsg = msg as IMessage2;
    if( iMsg2 != null )
    {
       string y = iMsg2.Where;
    }
}

谢谢

【问题讨论】:

  • 我认为它只会破坏创建了SMessage 派生类型的客户端(它不是密封的)。
  • 请在programmers.SE阅读versioning interfaces
  • 对于你的更新,你甚至不需要强制转换,msg 已经暴露了.Where 你可以直接使用string y = msg.Where; 没有问题。如果ReceiveChange 接受IMessage 而不是ChangeState,您只需要进行演员表。 (另外,你的演员阵容是错误的,你会得到一个例外,你应该这样做IMessage2 iMsg = msg as IMessage2;,如果演员阵容失败,as 将返回 null,而普通演员将引发运行时异常)
  • 非常感谢,但我仍然担心如果我更新服务器并且字段中有旧客户端在消息中没有 IMessage2 接口.. 服务器不会崩溃然后?换句话说,服务器有新的 IMessage2 消息...客户端有 IMessage 消息。客户端使用基于 IMessage 接口的消息调用服务器...服务器如何防止崩溃?

标签: c# class interface


【解决方案1】:

您的接口的消费者不会抱怨,但实现会抱怨。

如果您想避免这种情况,请创建一个从旧接口扩展的新接口:

public interface INewMessage : IMessage
{
    string Where { get; set; }
}

【讨论】:

    【解决方案2】:

    如果在 WebAPI 场景中。

    只有在接受 IMessage 作为参数的方法中依赖于新添加的字段时,它才会破坏现有客户端。

    public void ServiceMethod(IMessage message) {
       if (message.Where == null)
          throw new ArgumentException("message.Where is null");
    }
    

    您可以向接口添加内容,只要您有代码来正确处理现有客户端的缺失信息就可以了。

    不过,处理此问题的正确方法是对您的服务和数据合同进行“版本化”。我发现命名空间版本控制最容易维护。您将定义一个新的命名空间(例如 v2)并重新定义实际更改的所有内容、方法、数据协定等。然后在您的路由中,将 v2 消息(http://acme.com/api/v2/messages)路由到新的命名空间,或者如果没有特别路由(@ 987654323@) 将其路由到旧命名空间。

    如果在直接引用的库中。

    然后是的 - 它会破坏现有的客户。除非您生产具体实现的工厂可以确定客户想要的。类似于 WebAPI 路由的东西 - 但用于直接引用的库。但这是极其困难的。

    【讨论】:

      【解决方案3】:

      是的,如果他们实现自己的使用IMessage 而不是从SMessage 派生的类,它将破坏现有的客户端。这就是为什么微软没有在版本之间更新 .NET 框架中的接口以添加新功能的原因。例如,在 .NET 4.5 中,DbDataReader 获得了新的异步方法,这些方法返回任务但它们无法更新 IDataReader,因为这会破坏任何实现 IDataReader 而不从 DbDataReader 派生的人。

      如果您不想破坏使用IMessage 创建类但不使用SMessage 的人的代码,您必须创建一个具有附加字段的新派生接口(例如,这是 COM 对象所做的,你会经常看到ISomeInterfaceISomeInterface2ISomeInterface3等)或者根本不更新接口,只更新其他人可能派生的具体实现。

      【讨论】:

      • 那是因为微软没有实现工厂来创建和返回 IDataReader 实现,也没有实现版本控制。所有实现都是直接实例化的。如果工厂用于实例化接口的具体实现,那么您可以通过版本控制来更改接口。
      • @rick 我想不出我曾经直接创建过数据读取器,它始终来自.ExecuteDataReader().ToDataReader() 工厂方法。
      • 是的,你完全正确。但是这些方法返回 DbDataReader 的具体类,而不是 IDataReader。这就是它的症结所在。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-10-26
      • 1970-01-01
      • 1970-01-01
      • 2011-02-24
      • 2013-04-18
      • 2011-02-26
      相关资源
      最近更新 更多