我有一些基本的消息Enums,我将它放入一个界面来控制它们。但是,当我这样做时,我需要支持Legacy消息,同时仍然添加新消息。最好的方法是什么?
这是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
,或者更有意义效率立场,为NewMsg
枚举创建一个全新的New_Msg
界面
我意识到这两种解决方案都可行,但我想知道另一位开发人员在寻找那种解决方案,更有意义吗?
答案 0 :(得分:2)
接口无法覆盖,但您可以通过界面向合同添加新项目。
如果行为在实现之间发生变化,使得它们不向后兼容,那么你应该创建一个与旧的接口/实现没有任何关系的全新接口/实现,因为有人可以创建新实例,并且将它传递给期望旧的消费者。
单个实现可以实现两个接口以保持尽可能多的兼容性。如果接口具有成员的重叠,那么您可以使用显式接口实现来将两个接口维护在一起。
答案 1 :(得分:1)
这种结构可以为您提供更大的灵活性,同时保持您的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);
}
答案 2 :(得分:1)
但是我的问题是,为New_Msgs创建一个新接口更有意义,它继承了Msg并覆盖了MessageId
您不能拥有override
的界面,但是您可以使用new
,但这意味着两者都具有相同的签名,而不是这里的情况。
如果您正在定义新功能,那么它与之前的界面不向后兼容,那么我建议interface Msg2
之类的内容与interface Msg
无关(即不会继承)。由于类可以实现多个接口,因此您的实际实现可以处理两个接口实现,同时保持定义分离,从而允许任何将来的代码选择是否处理遗留实现。
答案 3 :(得分:1)
如果其他人正在使用此界面(我认为任何一种解决方案都应该没问题)我会建议使用“过时”标记来提醒其他开发者有关更新。
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
{
{
答案 4 :(得分:0)
您可以通过多种方式解决此问题:
您可以将代码重构为replace type code with subclasses。枚举基本上是普通常量,具有一点类安全性,不适合OOP和版本控制。在C#中,它们无法扩展:
public interface IMsg
{
// let `MessageType` be a class
MessageType Type { get; }
byte[] Data { get; }
}
或者,您可以使用普通的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;
作为旁注,这是一个.NET命名约定,可以为所有接口名称添加大写I
。