具有多个定义 C# 的接口

本文关键字:接口 定义 | 更新日期: 2023-09-27 18:37:07

我有几个基本的消息枚举,我正在将其放入接口中以控制它们。 但是,当我这样做时,我需要支持旧版消息,同时仍然添加新消息。 最好的方法是什么?

这是Msg接口的一般思路:

public interface Msg
{
    /// <summary>
    /// Gets the ID of the message
    /// </summary>
    Legacy_Msgs MessageId { get; }
    //New_Msgs MessageId { get; }    // How implement use this?
    /// <summary>
    /// Converts the message to a byte array representation
    /// </summary>
    byte[] MsgBytes { get; }
}

然而,我的问题是,为New_Msgs使用继承Msg并覆盖MessageId的新接口是否更有意义,或者从效率的角度来看,为New_Msg枚举提供一个全新的NewMsg接口是否更有意义。
我意识到这两种解决方案都可以工作,但我想知道作为另一位开发人员正在研究这种解决方案,什么更有意义?

具有多个定义 C# 的接口

接口无法覆盖,但您可以通过接口向合约添加新项。

如果行为将在实现之间发生变化,以至于它们不向后兼容,那么您应该创建一个与旧接口/实现没有关系的全新接口/实现,因为有人可以创建新接口/实现的实例,并将其传递给期望旧接口/实现的消费者。

单个实现可以实现这两个接口,以保持尽可能多的兼容性。如果接口的成员重叠,则可以使用显式接口实现来同时维护两个接口。

但是,我的问题是,为New_Msgs使用继承Msg并覆盖MessageId的新接口是否更有意义

您不能拥有带有override s的接口,但是您可以使用new但这意味着两者都具有相同的签名,而这里的情况并非如此。

如果您正在定义与以前的接口不向后兼容的新功能,那么我会建议像interface Msg2这样与interface Msg无关(即不继承)的东西。 由于一个类可以实现多个接口,因此您的实际实现可以处理这两个接口实现,同时保持定义独立,从而允许任何未来的代码选择是否处理遗留实现。

如果其他人正在使用此接口(我认为任何一种解决方案都应该没问题),我建议使用"过时"标签来提醒其他开发人员有关更新的信息。

http://msdn.microsoft.com/en-us/library/22kk2b44(v=vs.80).aspx

public interface INewMsgId : IMsgId
{
}
[System.Obsolete("use interface INewMsgId ")]
public interface ILegacyMsgId : IMsgId
{
{

这种结构可能会给你更大的灵活性,同时保持你的IMsg合同对消费者的一致性。

// the following three MsgId contracts don't have to be contracts at all (the actual types can be specified in generic IMsg directly), but if one ID type is wildly different in nature than the other, an interface such as this might make sense.
public interface IMsgId
{
 // ?
}
public interface INewMsgId : IMsgId
{
}
public interface ILegacyMsgId : IMsgId
{
}
public interface IMsg<out TId>
   where TId : IMsgId
{
   TId MessageId { get; }
   byte[] MsgBytes { get; }
}
// if it is sensible, you can use the following interfaces to create definitive new and legacy message contracts
public interface INewMsg : IMsg<INewMsgId>
{
}
public interface ILegacyMsg : IMsg<ILegacyMsgId>
{
}

下面是一个实现示例:

public class LegacyMsgId : ILegacyMsgId
{
    public LegacyMsgId(int id)
    {
      Id = id;
    }
    public int Id { get; private set; }

    public override string ToString()
    {
        return "Legacy Message #" + Id;
    }
}
public class NewMsgId : INewMsgId
{
    public NewMsgId(int id)
    {
        Id = id;
    }
    public int Id { get; private set; }
    public override string ToString()
    {
        return "New Message #" + Id;
    }
}
public class NewMsg : INewMsg
{
    public NewMsg(int id)
    {
        MessageId = new NewMsgId(id);
    }
    public NewMsgId MessageId { get; private set; }
    INewMsgId IMsg<INewMsgId>.MessageId { get { return MessageId; } }
    public byte[] MsgBytes { get; private set; }
}
public class LegacyMsg : ILegacyMsg
{
    public LegacyMsg(int id)
    {
        MessageId = new LegacyMsgId(id);
    }
    public LegacyMsgId MessageId { get; private set; }
    ILegacyMsgId IMsg<ILegacyMsgId>.MessageId { get { return MessageId; } }
    public byte[] MsgBytes { get; private set; }
}

和用法:

var messages = new List<IMsg<IMsgId>>();
messages.Add(new NewMsg(20));
messages.Add(new LegacyMsg(11));
foreach(var message in messages)
{
    Console.WriteLine(message.MessageId);
}

您可以通过多种方式处理此问题:

  1. 可以重构代码以将类型代码替换为子类。枚举基本上是具有少量类型安全性的普通常量,不适合 OOP 和版本控制。在 C# 中,它们不能扩展:

    public interface IMsg
    {
        // let `MessageType` be a class
        MessageType Type { get; }
        byte[] Data { get; }
    }
    
  2. 或者,您可以使用普通 int ID,并将特定 MessageID 背后的实际含义与包含数据的简单对象分开。

    // let Msg be a dumb data object
    public interface IMsg
    {
        int Id { get; }
        byte[] Data { get; }
    }
    // and then keep their types weakly-typed
    private readonly Dictionary<int, String> MessageNames;
    // and you can also provide various type-based functionality
    // during runtime
    private readonly Dictionary<int, IParser> Parsers;
    

作为旁注,它是在所有接口名称前面附加大写I的 .NET 命名约定。