C# 如何使用受约束的泛型类型参数将类实例化为其派生自的接口

C# 如何使用受约束的泛型类型参数将类实例化为其派生自的接口,c#,solid-principles,open-closed-principle,generic-variance,C#,Solid Principles,Open Closed Principle,Generic Variance,下面是定义数据包的接口 public interface IPacket { int Size { get; } } 有两个实现,每个实现都有自己的附加属性 public class FooPacket : IPacket { public int Size => 10; public string FooProperty { get; set; } } public class BarPacket : IPacket { public int Size

下面是定义数据包的接口

public interface IPacket
{
    int Size { get; }
}
有两个实现,每个实现都有自己的附加属性

public class FooPacket : IPacket
{
    public int Size => 10;
    public string FooProperty { get; set; }
}

public class BarPacket : IPacket
{
    public int Size => 20;
    public string BarProperty { get; set; }
}
以上是我无法控制的库代码。我想为数据包创建一个处理程序

public interface IPacketHandler<T> where T : IPacket
{
    void HandlePacket(T packet) ;
}

然后在实现中强制转换到特定的数据包?

问题的核心是,最终您将调用处理程序将一个具体的数据包(具体类型)作为参数传递给它,即使您将参数隐藏在
IPacket
后面

因此,尝试使用
BarPacket
参数调用
HandlePacket(foopack)
将不得不失败,唯一的问题是它何时/何地失败

正如您已经注意到的,将泛型参数引入数据包处理程序会使其在编译时失败,并且没有简单的解决方法

您删除泛型参数的想法,即

public interface IPacketHandler
{
   void HandlePacket(IPacket packet);
}
这是一个可能的解决办法。但是,它会将可能的故障推送到运行时,您现在必须在运行时检查是否使用不适当的参数调用了处理程序

您还可以通过引入契约使此运行时检查更加明确:

public interface IPacketHandler
{
   bool CanHandlePacket(IPacket packet);
   void HandlePacket(IPacket packet);
}
这使得消费者安全地调用
HandlePacket
——前提是他们之前调用
CanHandlePacket
得到了肯定的结果

例如,在数据包列表上进行一个可能的简单循环并调用您的处理程序将变得非常简单

foreach ( var packet in _packets )
  foreach ( var handler in _handlers )
    if ( handler.CanHandlePacket(packet) )
      handler.HandlePacket(packet);

你可以通过一点思考来解决这个问题

首先,为了方便(并稍微有助于类型安全),引入一个“标记”界面,您所有的
IPacketHandler
界面都将实现该界面:

public interface IPacketHandlerTag // "Tag" interface.
{
}
这并不是必须的,但这意味着以后可以使用
IEnumerable
而不是
IEnumerable
,这确实让事情变得更加明显

然后您的
IPacketHandler
界面变成:

public interface IPacketHandler<in T> : IPacketHandlerTag where T : IPacket
{
    void HandlePacket(T packet);
}
如果传递给
PacketHandlerManager
构造函数的数据包处理程序未使用可从
IPacket
分配的单个参数实现名为
HandlePacket
的方法,它将抛出
InvalidOperationException

例如,尝试使用以下类的实例将导致构造函数抛出:

public class BadPacketHandler: IPacketHandlerTag
{
    public void HandlePacket(string packet)
    {
        Console.WriteLine("Handling string");
    }
}
现在你可以这样叫“使用它”:

    var packetHandlers = new List<IPacketHandlerTag>
    {
        new FooPacketHandler(),
        new BarPacketHandler()
    };

    var manager = new PacketHandlerManager(packetHandlers);

    var foo = new FooPacket();
    var bar = new BarPacket();
    var baz = new BazPacket();

    manager.HandlePacket(foo);
    manager.HandlePacket(bar);
    manager.HandlePacket(baz);
var packetHandlers=新列表
{
新建FooPacketHandler(),
新的BarPacketHandler()
};
var manager=新的PacketHandlerManager(packetHandlers);
var foo=new FooPacket();
var bar=新的BarPacket();
var baz=new BazPacket();
手袋经理(foo);
经理:手袋(酒吧);
手袋经理(baz);

将所有内容整合到一个可编译的控制台应用程序中:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;

namespace ConsoleApp1
{
    public interface IPacket
    {
        int Size { get; }
    }

    public class FooPacket : IPacket
    {
        public int    Size        => 10;
        public string FooProperty { get; set; }
    }

    public class BarPacket : IPacket
    {
        public int    Size        => 20;
        public string BarProperty { get; set; }
    }

    public class BazPacket : IPacket
    {
        public int    Size        => 20;
        public string BazProperty { get; set; }
    }

    public interface IPacketHandlerTag // "Tag" interface.
    {
    }

    public interface IPacketHandler<in T> : IPacketHandlerTag where T : IPacket
    {
        void HandlePacket(T packet);
    }

    public class FooPacketHandler : IPacketHandler<FooPacket>
    {
        public void HandlePacket(FooPacket packet)
        {
            Console.WriteLine("Handling FooPacket");
        }
    }

    public class BarPacketHandler : IPacketHandler<BarPacket>
    {
        public void HandlePacket(BarPacket packet)
        {
            Console.WriteLine("Handling BarPacket");
        }
    }

    public class PacketHandlerManager
    {
        public PacketHandlerManager(IEnumerable<IPacketHandlerTag> packetHandlers)
        {
            foreach (var packetHandler in packetHandlers)
            {
                bool appropriateMethodFound = false;
                var handlerType = packetHandler.GetType();
                var allMethods  = handlerType.GetMethods(BindingFlags.Public | BindingFlags.Instance);

                foreach (var method in allMethods.Where(m => m.Name == "HandlePacket"))
                {
                    var args = method.GetParameters();

                    if (args.Length == 1 && typeof(IPacket).IsAssignableFrom(args[0].ParameterType))
                    {
                        _handlers.Add(args[0].ParameterType, item => method.Invoke(packetHandler, new object[]{item}));
                        appropriateMethodFound = true;
                    }
                }

                if (!appropriateMethodFound)
                    throw new InvalidOperationException("No appropriate HandlePacket() method found for type " + handlerType.FullName);
            }
        }

        public void HandlePacket(IPacket packet)
        {
            if (_handlers.TryGetValue(packet.GetType(), out var handler))
            {
                handler(packet);
            }
            else
            {
                Console.WriteLine("No handler found for packet type " + packet.GetType().FullName);
            }
        }

        readonly Dictionary<Type, Action<IPacket>> _handlers = new Dictionary<Type, Action<IPacket>>(); 
    }

    class Program
    {
        public static void Main()
        {
            var packetHandlers = new List<IPacketHandlerTag>
            {
                new FooPacketHandler(),
                new BarPacketHandler()
            };

            var manager = new PacketHandlerManager(packetHandlers);

            var foo = new FooPacket();
            var bar = new BarPacket();
            var baz = new BazPacket();

            manager.HandlePacket(foo);
            manager.HandlePacket(bar);
            manager.HandlePacket(baz);
        }
    }
}
使用系统;
使用System.Collections.Generic;
使用System.Linq;
运用系统反思;
名称空间控制台EAPP1
{
公共接口IPacket
{
整数大小{get;}
}
公共类文件包:IPacket
{
公共整数大小=>10;
公共字符串属性{get;set;}
}
公共类条码包:IPacket
{
公共整数大小=>20;
公共字符串属性{get;set;}
}
公共类数据包:IPacket
{
公共整数大小=>20;
公共字符串属性{get;set;}
}
公共接口IPacketHandlerTag/“Tag”接口。
{
}
公共接口IPacketHandler:IPacketHandler标记,其中T:IPacket
{
无效手包(T包);
}
公共类FooPacketHandler:IPacketHandler
{
公共无效HandlePacket(FooPacket数据包)
{
控制台写入线(“处理数据包”);
}
}
公共类BarPacketHandler:IPacketHandler
{
公共无效手包(BarPacket)
{
控制台。WriteLine(“处理BarPacket”);
}
}
公共类PacketHandlerManager
{
公共PacketHandlerManager(IEnumerable packetHandlers)
{
foreach(packetHandlers中的var packetHandler)
{
bool-approvedmethodfound=false;
var handlerType=packetHandler.GetType();
var allMethods=handlerType.GetMethods(BindingFlags.Public | BindingFlags.Instance);
foreach(allMethods.Where(m=>m.Name==“HandlePacket”)中的var方法)
{
var args=method.GetParameters();
if(args.Length==1&&typeof(IPacket).IsAssignableFrom(args[0].ParameterType))
{
_Add(args[0].ParameterType,item=>method.Invoke(packetHandler,新对象[]{item}));
找到的方法=真;
}
}
如果(!找到合适的方法)
抛出新的InvalidOperationException(“没有为类型“+handlerType.FullName”找到合适的HandlePacket()方法);
}
}
公共无效手包(IPacket包)
{
if(_handlers.TryGetValue(packet.GetType(),out var handler))
{
处理程序(数据包);
}
其他的
{
WriteLine(“未找到包类型“+packet.GetType().FullName”的处理程序);
}
}
只读字典_handlers=new Dictionary();
}
班级计划
{
公共静态void Main()
{
var packetHandlers=新列表
{
新建FooPacketHandler(),
新的BarPacketHandler()
};
var manager=新的PacketHandlerManager(packetHandlers);
var foo=new FooPacket();
var bar=新的BarPacket();
变量
public interface IPacketHandlerTag // "Tag" interface.
{
}
public interface IPacketHandler<in T> : IPacketHandlerTag where T : IPacket
{
    void HandlePacket(T packet);
}
public class PacketHandlerManager
{
    public PacketHandlerManager(IEnumerable<IPacketHandlerTag> packetHandlers)
    {
        foreach (var packetHandler in packetHandlers)
        {
            bool appropriateMethodFound = false;
            var handlerType = packetHandler.GetType();
            var allMethods  = handlerType.GetMethods(BindingFlags.Public | BindingFlags.Instance);

            foreach (var method in allMethods.Where(m => m.Name == "HandlePacket"))
            {
                var args = method.GetParameters();

                if (args.Length == 1 && typeof(IPacket).IsAssignableFrom(args[0].ParameterType))
                {
                    _handlers.Add(args[0].ParameterType, item => method.Invoke(packetHandler, new object[]{item}));
                    appropriateMethodFound = true;
                }
            }

            if (!appropriateMethodFound)
                throw new InvalidOperationException("No appropriate HandlePacket() method found for type " + handlerType.FullName);
        }
    }

    public void HandlePacket(IPacket packet)
    {
        if (_handlers.TryGetValue(packet.GetType(), out var handler))
        {
            handler(packet);
        }
        else
        {
            Console.WriteLine("No handler found for packet type " + packet.GetType().FullName);
        }
    }

    readonly Dictionary<Type, Action<IPacket>> _handlers = new Dictionary<Type, Action<IPacket>>(); 
}
public class BadPacketHandler: IPacketHandlerTag
{
    public void HandlePacket(string packet)
    {
        Console.WriteLine("Handling string");
    }
}
    var packetHandlers = new List<IPacketHandlerTag>
    {
        new FooPacketHandler(),
        new BarPacketHandler()
    };

    var manager = new PacketHandlerManager(packetHandlers);

    var foo = new FooPacket();
    var bar = new BarPacket();
    var baz = new BazPacket();

    manager.HandlePacket(foo);
    manager.HandlePacket(bar);
    manager.HandlePacket(baz);
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;

namespace ConsoleApp1
{
    public interface IPacket
    {
        int Size { get; }
    }

    public class FooPacket : IPacket
    {
        public int    Size        => 10;
        public string FooProperty { get; set; }
    }

    public class BarPacket : IPacket
    {
        public int    Size        => 20;
        public string BarProperty { get; set; }
    }

    public class BazPacket : IPacket
    {
        public int    Size        => 20;
        public string BazProperty { get; set; }
    }

    public interface IPacketHandlerTag // "Tag" interface.
    {
    }

    public interface IPacketHandler<in T> : IPacketHandlerTag where T : IPacket
    {
        void HandlePacket(T packet);
    }

    public class FooPacketHandler : IPacketHandler<FooPacket>
    {
        public void HandlePacket(FooPacket packet)
        {
            Console.WriteLine("Handling FooPacket");
        }
    }

    public class BarPacketHandler : IPacketHandler<BarPacket>
    {
        public void HandlePacket(BarPacket packet)
        {
            Console.WriteLine("Handling BarPacket");
        }
    }

    public class PacketHandlerManager
    {
        public PacketHandlerManager(IEnumerable<IPacketHandlerTag> packetHandlers)
        {
            foreach (var packetHandler in packetHandlers)
            {
                bool appropriateMethodFound = false;
                var handlerType = packetHandler.GetType();
                var allMethods  = handlerType.GetMethods(BindingFlags.Public | BindingFlags.Instance);

                foreach (var method in allMethods.Where(m => m.Name == "HandlePacket"))
                {
                    var args = method.GetParameters();

                    if (args.Length == 1 && typeof(IPacket).IsAssignableFrom(args[0].ParameterType))
                    {
                        _handlers.Add(args[0].ParameterType, item => method.Invoke(packetHandler, new object[]{item}));
                        appropriateMethodFound = true;
                    }
                }

                if (!appropriateMethodFound)
                    throw new InvalidOperationException("No appropriate HandlePacket() method found for type " + handlerType.FullName);
            }
        }

        public void HandlePacket(IPacket packet)
        {
            if (_handlers.TryGetValue(packet.GetType(), out var handler))
            {
                handler(packet);
            }
            else
            {
                Console.WriteLine("No handler found for packet type " + packet.GetType().FullName);
            }
        }

        readonly Dictionary<Type, Action<IPacket>> _handlers = new Dictionary<Type, Action<IPacket>>(); 
    }

    class Program
    {
        public static void Main()
        {
            var packetHandlers = new List<IPacketHandlerTag>
            {
                new FooPacketHandler(),
                new BarPacketHandler()
            };

            var manager = new PacketHandlerManager(packetHandlers);

            var foo = new FooPacket();
            var bar = new BarPacket();
            var baz = new BazPacket();

            manager.HandlePacket(foo);
            manager.HandlePacket(bar);
            manager.HandlePacket(baz);
        }
    }
}
public enum PacketType
{
    Foo,
    Bar
}

public interface IPacket
{
    PacketType Type { get; }
}

public class FooPacket : IPacket
{
    public PacketType Type => PacketType.Foo;
    public string FooProperty { get; }
}

public class BarPacket : IPacket
{
    public PacketType Type => PacketType.Bar;
    public string BarProperty { get; }
}
public interface IPacketHandler
{
    void HandlePacket(IPacket packet);
}

public abstract class PacketHandler<T> : IPacketHandler where T : IPacket
{
    public abstract PacketType HandlesPacketType { get; }

    public void HandlePacket(IPacket packet)
    {
        if (packet is T concretePacket)
        {
            HandlePacket(concretePacket);
        }
    }

    protected abstract void HandlePacket(T packet);
}

public class FooPacketHandler : PacketHandler<FooPacket>
{
    public override PacketType HandlesPacketType => PacketType.Foo;

    protected override void HandlePacket(FooPacket packet) { /* some logic that accesses FooProperty */ }
}

public class BarPacketHandler : PacketHandler<BarPacket>
{
    public override PacketType HandlesPacketType => PacketType.Bar;

    protected override void HandlePacket(BarPacket packet) { /* some logic that accesses BarProperty */ }
}

public class PacketHandlerManager
{
    public PacketHandlerManager(Library library, IEnumerable<IPacketHandler> packetHandlers)
    {
        foreach (var packetHandler in packetHandlers)
        {
            library.Bind(packetHandler.HandlesPacketType, packetHandler.HandlePacket);
        }
    }
}