I have created a cycle manager using this code
public class CycleManager
{
private ConcurrentQueue<BaseCommand> m_Commands = new ConcurrentQueue<BaseCommand>();
private EventContainer<BaseEvent> m_Container = new EventContainer<BaseEvent>();
public void RunCommand(BaseCommand command)
{
m_Commands.Enqueue(command);
}
public void WaitForEvent<T>(Action<T> handler) where T : BaseEvent
{
m_Container.Add(handler);
}
}
The event contain look like this:
public class EventContainer<TEvent> where TEvent : BaseEvent
{
private Dictionary<Type, Action<TEvent>> m_eventHandlers = new Dictionary<Type, Action<TEvent>>();
public void Add(Action<TEvent> handler)
{
m_eventHandlers.Add(typeof(TEvent), handler);
}
}
I'm getting error
Argument 1: cannot convert from 'System.Action' to 'System.Action<CycleManagerTesting.Models.BaseEvent>'
in line m_Container.Add(handler);
I don't know how to do what I want . I just want to be able to do
var c = new CycleManager();
c.WaitForEvent<DoorOpenedEvent>(e =>
{
// something with e.DoorID;
});
I know one of the solutions is:
public void Add(Action<BaseEvent> handler)
{
m_eventHandlers.Add(typeof(TEvent), handler);
}
but i don't want to cast everytime I'm using the method
var c = new CycleManager();
c.WaitForEvent<DoorOpenedEvent>(e =>
{
var realevent = (DoorOpenedEvent)e;
});
public class DoorOpenedEvent:BaseEvent
{
public string DoorID { get; set; }
public string Status { get; set; }
}
public class BaseEvent{}
The method EventContainer<BaseEvent>.Add expects a Action<BaseEvent> delegate.
However, the WaitForEvent method would accept and therefore try to add not only Action<BaseEvent>, but also Action<DerivedEvent> due the generic where T : BaseEvent constraint restricting T to be BaseEvent or any other possible type derived from BaseEvent (like class DerivedEvent : BaseEvent { }, for example).
But despite the WaitForEvent method accepting a delegate like Action<DerivedEvent>, it is simply impossible to pass on such a delegate to the EventContainer<BaseEvent>.Add method, because Action<DerivedEvent> is not convertible to Action<BaseEvent>.
One possible solution is to let the EventContainer<TEvent>.Add method accept not only Action<T>, but also "Action<derived from TEvent>". The latter will be wrapped in an anonymous or local function that itself accepts a TEvent parameter and thus is convertible to an Action<TEvent> (which then can be added to the dictionary). To enable this, a slight change in the signature of the Add method is required, making the method itself generic:
public class EventContainer<TEvent> where TEvent : BaseEvent
{
...
public void Add<T>(Action<T> handler) where T : TEvent
{
m_eventHandlers.Add(
typeof(T),
(handler is Action<TEvent> handlerForTEvent)
? handlerForTEvent
: InvokeHandlerWithDerivedEvent
);
void InvokeHandlerWithDerivedEvent(TEvent baseEvt)
{
if (baseEvt is T evt)
{
handler(evt);
}
else
{
// Ouch!
// That is not supposed to happen.
// Throw some exception or something...
}
}
}
Related
I have a basic architecture for 'orders' in my game - each order has a basic data type and an 'order processor' which performs some action based on the given data. For example 'move to this point.'
I'd like to have a tidy system where I can simply pass in order data and the correct order processor will kick in, in such a way that I can easily add new OrderData/OrderProcessor classes without modifying other classes or messing around with enums or casting, so it seems like generics are the way to go.
This is the current code I have. I'm drawing a blank for how to draw a connection in the 'OrderService' class between the correct 'OrderProcessor' and 'IOrderData' pairs. See inside the OrderService<T> class for more information:
public interface IOrderData
{
}
// an order processor reads data from an IOrderData object until the data object says stop
public abstract class OrderProcessor<T> where T : IOrderData
{
protected T m_currentData;
public virtual void Start(T data)
{
m_currentData = data;
}
}
////////////////////////
public class MoveOrderData : IOrderData
{
public Vector3 Destination { get; private set; }
}
public class MoveOrderProcessor : OrderProcessor<MoveOrderData>
{
public override void Start(MoveOrderData data)
{
base.Start(data);
}
}
////////////////////////
public class OrderService<T> where T : IOrderData
{
private Dictionary<System.Type, OrderProcessor<T>> m_processors = new Dictionary<System.Type, OrderProcessor<T>>();
private OrderProcessor<T> m_currentProcessor;
public void GiveOrder(IOrderData data)
{
// this is the main problem: I'm not sure how to say "the given data is type 'MoveOrderData' so find out which
// OrderProcessor class handles those and pass it in". A simple switch statement and cast would suffice here
// but I'd like to automate the process
}
}
A possible solution here can be a making an OrderService class non-generic, because it can handle a different types of orders/order processors (according to comments). Then maintain a Dictionary<Type, Action<object>>, which wraps Action<object> to call Start method from OrderProcessor<T>.
I've extended the original code a little bit to show how it can work
public interface IOrderData
{
}
public class MoveOrderData : IOrderData
{
public Vector3 Destination { get; private set; }
}
public class AttackOrderData : IOrderData
{
}
public abstract class OrderProcessor<T> where T : IOrderData
{
protected T CurrentData { get; set; }
public virtual void Start(T data)
{
CurrentData = data;
}
}
public class MoveOrderProcessor : OrderProcessor<MoveOrderData>
{
}
public class AttackOrderProcessor : OrderProcessor<AttackOrderData>
{
}
public class OrderService
{
private readonly Dictionary<Type, Action<object>> m_processors = new Dictionary<Type, Action<object>>();
public OrderService()
{
AddProcessor(new MoveOrderProcessor());
AddProcessor(new AttackOrderProcessor());
}
private void AddProcessor<T>(OrderProcessor<T> processor) where T : IOrderData
{
var action = (Action<T>)processor.Start;
m_processors.Add(typeof(T), obj => action((T)obj));
}
public void GiveOrder(IOrderData data)
{
var action = m_processors[data.GetType()];
action?.Invoke(data);
}
}
It causes a downcasting obj => action((T)obj), but it shouldn't be a problem, since your data is constrained to IOrderData interface. Example of the usage
var service = new OrderService();
service.GiveOrder(new MoveOrderData());
service.GiveOrder(new AttackOrderData());
There's the following interface which defines a packet.
public interface IPacket
{
int Size { get; }
}
There are two implementations, each with its own additional property.
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; }
}
The above is library code I have no control over. I want to create a handler for packets
public interface IPacketHandler<T> where T : IPacket
{
void HandlePacket(T packet) ;
}
and create two implementations for the concrete packets.
public class FooPacketHandler : IPacketHandler<FooPacket>
{
public void HandlePacket(FooPacket packet) { /* some logic that accesses FooProperty */ }
}
public class BarPacketHandler : IPacketHandler<BarPacket>
{
public void HandlePacket(BarPacket packet) { /* some logic that accesses BarProperty */ }
}
I'd like to inject a list of packet handlers into a class that manages packet handling so that it can be extended in the future with additional packet handlers.
public class PacketHandlerManager
{
public PacketHandlerManager(IEnumerable<IPacketHandler<IPacket>> packetHandlers)
{
}
}
The trouble I'm having is when creating the injected parameter. I cannot do
var packetHandlers = new List<IPacketHandler<IPacket>>
{
new FooPacketHandler(),
new BarPacketHandler()
};
because I cannot create an instance like so:
IPacketHandler<IPacket> packetHandler = new FooPacketHandler();
I get the error Cannot implicitly convert type 'FooPacketHandler' to 'IPacketHandler<IPacket>. An explicit conversion exists (are you missing a cast?)
I had a look at a similar question: Casting generic type with interface constraint. In that question, OP didn't show the members of the interface, only the definition of it from a generics point of view. From what I can see, if my interface didn't use the generic type parameter as an input, I could make it covariant using the out keyword, but that doesn't apply here.
How do I achieve making manager adhere to the open-closed principle? Is my only recourse changing the interface definition to
public interface IPacketHandler
{
void HandlePacket(IPacket packet);
}
and then casting to a particular packet in the implementation?
The core of the issue is that ultimately you would call your handler passing a concrete packet (of a concrete type) to it as an argument, even though you hide the argument behind IPacket.
Somehow then, trying to call the HandlePacket( FooPacket ) with BarPacket argument would have to fail, the only question is when/where it fails.
As you already noticed, introducing the generic parameter to the packet handler makes it fail in the compile time and there is no easy workaround over it.
Your idea to drop the generic parameter, i.e. to have
public interface IPacketHandler
{
void HandlePacket(IPacket packet);
}
is a possible solution. It however pushes the possible failure to the runtime, where you now have to check if a handler is called with inappropriate argument.
What you could also do is to make this runtime check more explicit by introducing a contract for it:
public interface IPacketHandler
{
bool CanHandlePacket(IPacket packet);
void HandlePacket(IPacket packet);
}
This makes it cleaner for the consumer to safely call HandlePacket - assuming they get a positive result from calling CanHandlePacket before.
For example, a possible naive loop over a list of packets and calling your handlers would become
foreach ( var packet in _packets )
foreach ( var handler in _handlers )
if ( handler.CanHandlePacket(packet) )
handler.HandlePacket(packet);
You can solve this with a little bit of reflection.
Firstly, for convenience (and to help slightly with type-safety), introduce a "Tag" interface which all your IPacketHandler<T> interfaces will implement:
public interface IPacketHandlerTag // "Tag" interface.
{
}
This is not really necessary, but it means you can use IEnumerable<IPacketHandlerTag> instead of IEnumerable<object> later on, which does make things a little more obvious.
Then your IPacketHandler<T> interface becomes:
public interface IPacketHandler<in T> : IPacketHandlerTag where T : IPacket
{
void HandlePacket(T packet);
}
Now you can write a PacketHandlerManager that uses reflection to pick out the method to use to handle a packet, and add it to a dictionary like so:
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>>();
}
If a packet handler passed to the PacketHandlerManager constructor does not implement a method called HandlePacket with a single argument that is assignable from IPacket, it will throw an InvalidOperationException.
For example, attempting to use an instance of the following class would cause the constructor to throw:
public class BadPacketHandler: IPacketHandlerTag
{
public void HandlePacket(string packet)
{
Console.WriteLine("Handling string");
}
}
Now you can call use it thusly:
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);
Putting it all together into a compilable console app:
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);
}
}
}
The output of this is:
Handling FooPacket
Handling BarPacket
No handler found for packet type ConsoleApp1.BazPacket
Thanks for the answers. The solution I ended up with is this, starting with the library code:
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; }
}
The above version is a better approximation of the real thing.
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);
}
}
}
There's some more logic in PacketHandlerManager which I've omitted here. library dispatches packets to handlers, so I don't have to deal with that explicitly after I register handlers using the Bind method.
It's not exactly what I imagined, but it'll do.
Let's say I have a following class:
public class Handler
{
public Type RequesterType { get; }
public Handler(Type requesterType)
{
RequesterType = requesterType;
}
}
which is itself a dependency of another class:
public class Controller
{
public Handler Handler { get; }
public Controller(Handler handler)
{
Handler = handler;
}
}
Is it possible to register Handler in such a way that when Controller is being resolved, the dependent Handler's constructor argument requesterType is being assigned with Controller type?
Here's an example of what I want to achieve:
class Program
{
static void Main(string[] args)
{
var container = new WindsorContainer();
container.Register(
Component.For<Controller>()
//Component.For<Handler>(), <- this is the essense of my question
);
var controller = container.Resolve<Controller>();
Console.WriteLine(controller.Handler.RequesterType); //Should output Program.Controller
Console.ReadKey();
}
}
A common way to handle this would be to make Handler generic:
public class Handler<T>
{
public Type RequesterType { get { return typeof(T) ; }
}
Then instead of passing the type in the constructor, you'd pass it as a generic type parameter:
public class Controller
{
public Handler<Controller> Handler { get; }
public Controller(Handler<Controller> handler)
{
Handler = handler;
}
}
Now you can register your handlers per controller in the usual way, and they will resolve for the right type.
You may want to embellish this a bit with interfaces or covariance, but that is the general idea.
I have the following code:
public interface IMyActionFactory
{
AbstractAction<T> CreateAction<T>(MyActionParamBase paramBase = null)
where T : MyActionParamBase;
}
public sealed class MergeActionParam : MyActionParamBase
{
}
public class MergeTest
{
private readonly IMyActionFactory _actionFactory = new DefaultMyActionFactory();
[Theory]
[PropertyData("MergeWorksData")]
public void MergeWorks(/*params here*/)
{
var param = new MergeActionParam();
// populate param here
var sut = _actionFactory.CreateAction<MergeActionParam>(param);
sut.DoAction();
}
}
I am getting an error
"..Error 10 Using the generic type 'IMyActionFactory' requires 1
type arguments..."
Why does the compiler expect a type to be passed to my IMyActionFactory, since I have declared the interface without a T? As far as the method is concerned its the only one to expect the type. Am I missing something here?
How can I make it work without redefining the interface signature?
EDIT:
Feeling a bit embarassed here, because the quick code I put down and ran seperately in a standalone online c# compiler doesnt give any compilation errors. However, going back to my original solution (tens of projects altogether) the error is still there.. Maybe has something to do with the XUnit ?..not sure
public interface IMyActionFactory
{
AbstractAction<T> CreateAction<T>(MyActionParamBase paramBase = null)
where T : MyActionParamBase;
}
public interface IAction
{
void DoAction();
}
public abstract class AbstractAction<T> : IAction
where T : MyActionParamBase
{
public void DoAction()
{
}
}
public class MyActionParamBase
{
public MyActionParamBase()
{
}
}
public sealed class MergeActionParam : MyActionParamBase
{
}
public class DefaultMyActionFactory : IMyActionFactory
{
public AbstractAction<T> CreateAction<T>(MyActionParamBase paramBase = null) where T : MyActionParamBase
{
return null;
}
}
public class MergeTest
{
private readonly IMyActionFactory _actionFactory = new DefaultMyActionFactory();
public void MergeWorks(/*params here*/)
{
var param = new MergeActionParam();
// populate param here
var sut = _actionFactory.CreateAction<MergeActionParam>(param);
sut.DoAction();
}
}
This is what I want to do in C# (within class Helper - without generic arguments),
List<AbstractClass<dynamic>> data;
public void Add<T>(AbstractClass<T> thing)
{
this.data.Add((AbstractClass<dynamic>) thing);
}
This helper class would take and work with AbstractClass<> objects and give back AbstractClass<> of specific generic type. AbstractClass<T> contains many functions which return T / take in T like public T Invoke().
For Helper class T cannot be known beforehand. The Add<T>(.. thing) function is not in a class of type T.
To be used like this in Helper class's functions,
foreach(var c in data.Where(x => ...))
{
// public T Invoke() { ... } function within AbstractClass<T>
var b = c.Invoke();
// logic
}
This also fails,
List<AbstractClass<object>> data;
public void Add<T>(AbstractClass<T> thing)
{
this.data.Add((AbstractClass<object>) thing);
}
Now I think I can have,
List<dynamic> data; // or List<object> data;
public void Add<T>(AbstractClass<T> thing)
{
this.data.Add(thing);
}
but I want the constraint that List named data has only elements of type like
ConcreteClass : AbstractClass<OtherClass>
So we would know that there is an public T Invoke() function but we do not know what it returns. This is helpful to avoid mistakes of say misspelling Invocke and only knowing at run-time.
I want to avoid casting to dynamic every time to invoke functions that give back generic type T
To do what you want to do you are going to need to use a Contravariant interface
public class Program
{
static void Main()
{
var m = new Helper();
m.Add(new ConcreteClass());
m.Process();
}
class Helper
{
List<IAbstractClass<OtherClassBase>> data = new List<IAbstractClass<OtherClassBase>>();
public void Add(IAbstractClass<OtherClassBase> thing)
{
this.data.Add(thing);
}
public void Process()
{
foreach(var c in data.Where(x => x.ShouldBeProcessed()))
{
var b = c.Invoke();
Console.WriteLine(b.Question);
var castData = b as OtherClass;
if (castData != null)
Console.WriteLine(castData.Answer);
}
}
}
public interface IAbstractClass<out T>
{
bool ShouldBeProcessed();
T Invoke();
}
abstract class AbstractClass<T> : IAbstractClass<T>
{
public bool ShouldBeProcessed()
{
return true;
}
public abstract T Invoke();
}
class ConcreteClass : AbstractClass<OtherClass>
{
public override OtherClass Invoke()
{
return new OtherClass();
}
}
class OtherClassBase
{
public string Question { get { return "What is the answer to life, universe, and everything?"; } }
}
class OtherClass : OtherClassBase
{
public int Answer { get { return 42; } }
}
}
You do not need to tell Add what kind of class you are passing it, all that matters is it derives from the type specified. You could do public void Add(IAbstractClass<object> thing) and every class would work, but Invoke() would only return objects inside the foreach loop.
You need to figure out what is the most derived class you want Invoke() to return and that is what you set as the type in the list.
Maybe this will work for you:
public class Program
{
static void Main()
{
var m1 = new Helper<OtherClass>();
m1.Add(new ConcreteClass());
var m2 = new Helper<int>();
m2.Add(new ConcreteClass2());
}
class Helper<T>
{
List<AbstractClass<T>> data = new List<AbstractClass<T>>();
public void Add<T1>(T1 thing) where T1 : AbstractClass<T>
{
this.data.Add(thing);
}
}
class AbstractClass<T> { }
class OtherClass { }
class ConcreteClass : AbstractClass<OtherClass> { }
class ConcreteClass2 : AbstractClass<int> { }
}