我正在尝试构建一个功能包解析器。我有一个基类Datagram,现在我天真地以为我会像这样定义它:
(编辑注意事项,这个类不是抽象的!)
public class Datagram
{
public abstract static Datagram CreateFromDatagram(Datagram datagram);
}然后,对于特定的数据报,例如以太网和Tcp:
public class EthernetDatagram : Datagram, IPayloadDatagram
{
public override static Datagram CreateFromDatagram(Datagram datagram)
{
return new EthernetDatagram();
}
public Datagram Payload { get; }
}
public class TcpDatagram : Datagram, IPayloadDatagram
{
public overrides static Datagram CreateFromDatagram(Datagram datagram)
{
return new TcpDatagram();
}
public Datagram Payload { get; }
}这种(不可能的)抽象静态方法的原因是,我希望有一个扩展方法,允许我将所有这些数据包“链接”在一起:
public static class DatagramExtensions
{
public static T As<T>(this IPayloadDatagram datagram) where T : Datagram
{
return (T)T.CreateFromDatagram(datagram.Payload);
}
}因此,要拥有一个全新的数据报类型ANewDatagram,我所需要做的就是让它定义它的工厂方法CreateFromDatagram,然后我将能够愉快地使用我的函数扩展:
SomeDatagram.As<EthernetDatagram>().As<TcpDatagram>().As<ANewDatagram>()... 这一切都是可扩展的。
考虑到由于我无法继承抽象类,这是行不通的,那么除了实例化这样的泛型类之外,还有什么好的替代方法呢?
我可以使用反射,但这是隐藏它对用户。当我尝试创建ANewDatagram时,我必须记住,我稍后要反映一个CreateFromDatagram方法。
我目前正在使用反射来获取构造函数--但是我没有办法强制使用一个特定的构造函数来获取有效负载。如果有人创建了一个新的Datagram,不能保证他们添加了正确的构造函数,我必须在注释中告诉他们这一点,这很可能会被忽略,而失败的点是在最近的可能点上的运行时。
是否有更好的选择,体系结构,或某种形式的接口/继承,可以绕过这个问题?
(如果有人想看到我正在使用的完整源代码,我会尝试将这些扩展添加到包解释库中,作为https://github.com/PcapDotNet/Pcap.Net的一部分,尽可能少的修改)
发布于 2017-11-14 12:11:20
我会提出一个不同的解决方案。我会申请Depdency反演原理
使用新解决方案更新:
public class Datagram
{
public byte[] Data { get; set; }
}
public interface IPayload
{
Datagram Payload { get; }
}
public interface IConvertible
{
IPayload Convert(IPayload load);
}
public class EthernetDatagram : IPayload , IConvertible
{
public Datagram Payload
{
get
{
return null;
}
}
public IPayload Convert(IPayload load)
{
return new EthernetDatagram();
}
}
public class TcpDatagram : IConvertible, IPayload
{
public Datagram Payload
{
get
{
return null;
}
}
public IPayload Convert(IPayload load)
{
return null;
}
}
public static class Extension
{
public static IPayload As<T>(this IPayload load) where T : class, IConvertible, new()
{
IConvertible conv = new T();
return conv.Convert(load);
}
}
class Program
{
static void Main(string[] args)
{
IPayload load = new TcpDatagram();
var result = load.As<EthernetDatagram>();
}
}有了这个解决方案,你就走了相反的路。通过传递要“转换”到并拥有完全控制的具体类型,您可以离开硬反射的路径,并将其移动到不同的抽象层。对于新的数据报,您所要做的就是实现这两个接口,只要您希望在它们之间进行转换。这个更适合你的问题吗?
更新到注释:
新的解决办法将克服前一种解决办法的缺点。不通过泛型参数反映和说明“转换”类型。您可以连锁调用'As‘在一起,以达到您想要的准确。您现在唯一需要提供的是接口实现,默认的ctor,也就是它。
发布于 2017-11-14 10:11:06
..。但是,我没有办法强制执行,因为有一个特定的构造函数可以接受有效负载。
您可以通过在基类抽象类中声明适当的构造函数来强制执行它。
我还建议对您的代码进行一些修改。由于所有派生类都应该具有相同的Payload属性,所以请在基类Datagram类中声明它。还可以考虑将Datagram类声明为实现IPayloadDatagram接口。这样就没有必要将每个派生类标记为实现此接口。
下面是示例代码,希望它能满足您的需要:
public interface IPayloadDatagram
{
Datagram Payload { get; }
}
public abstract class Datagram : IPayloadDatagram
{
public Datagram Payload { get; }
protected Datagram(Datagram datagram)
{
Payload = datagram;
}
}
public class EthernetDatagram : Datagram
{
public EthernetDatagram(Datagram datagram) : base(datagram)
{
}
}
public static class DatagramExtensions
{
public static T As<T>(this IPayloadDatagram datagram) where T : Datagram
{
return (T)Activator.CreateInstance(typeof(T), datagram.Payload);
}
}发布于 2017-11-14 15:41:02
如果您有一个静态方法,但是该方法引用了一个实现定义的操作,那该怎么办?
这增加了编译时间检查,因此用户至少应该知道转换。
public interface IPayloadDatagram
{
Datagram Payload { get; }
}
public abstract class Datagram
{
public static Datagram CreateFromDatagram(Datagram datagram)
{
var action = datagram.GetConverter();
return action(datagram);
}
protected abstract Func<Datagram, Datagram> GetConverter();
}
public class EthernetDatagram : Datagram, IPayloadDatagram
{
protected override Func<Datagram, Datagram> GetConverter()
{
return x => new EthernetDatagram();
}
public Datagram Payload { get; set; }
}
public class TcpDatagram : Datagram, IPayloadDatagram
{
protected override Func<Datagram, Datagram> GetConverter()
{
return x => new TcpDatagram();
}
public Datagram Payload { get; set; }
}
public static class DatagramExtensions
{
public static T As<T>(this IPayloadDatagram datagram) where T : Datagram
{
return (T)Datagram.CreateFromDatagram(datagram.Payload);
}
}https://stackoverflow.com/questions/47282640
复制相似问题