Introduction
I started development with C# 2 months ago. Coming from a PHP background using Symfony I have knowledge about dependency injection and love generics so far, but the combination of the two confuses me a bit at the moment.
Concept
I want to use a factory to create a specific instance of an IHandler. The IHandler can receive an IParser<T> which - as you can see - supports generics, so I have one interface for every possible parser. The factory will get a list of IParser which have a SupportsParsing method which will be called inside the factory to get the correct IParser and create a new IHandler with the chosen IParser.
Code
IServiceFactory
public interface IServiceFactory<out T>
{
T Create(string key);
}
EventHandlerFactory
public class EventHandlerFactory<T> : IServiceFactory<IHandler>
{
private readonly IEnumerable<IParser<T>> _parsers;
public EventHandlerFactory(IEnumerable<IParser<T>> parsers)
{
_parsers = parsers;
}
public IHandler Create(string key)
{
foreach (IParser<T> parser in _parsers)
{
if (parser.SupportsParsing(key))
{
return new EventHandler<T>(parser);
}
}
throw new NotImplementedException();
}
}
IParser
public interface IParser<T>
{
List<T> Parse();
bool SupportsParsing(string key);
}
EventCounterParser
public class EventCounterParser : IParser<EventCounter>
{
public List<EventCounter> Parse()
{
throw new System.NotImplementedException();
}
public bool SupportsParsing(string key)
{
throw new System.NotImplementedException();
}
}
EventLevelParser
public class EventLevelParser : IParser<EventLevel>
{
public List<EventLevel> Parse()
{
throw new System.NotImplementedException();
}
public bool SupportsParsing(string key)
{
throw new System.NotImplementedException();
}
}
IHandler
public interface IHandler
{
void Handle();
}
EventHandler
public class EventHandler<T> : IHandler
{
private readonly IParser<T> _parser;
public EventHandler(IParser<T> parser)
{
_parser = parser;
}
public void Handle()
{
throw new System.NotImplementedException();
}
}
Registering the services
So registering the parsers is easy
services.AddScoped<IParser<EventCounter>, EventCounterParser>();
services.AddScoped<IParser<EventLevel>, EventLevelParser>();
Intention
But what I want to do now is:
* Register the service factory
* Register the handlers
* Automatically inject all IParser instances into the EventHandlerFactory
And this is where it's getting hard for me. I don't really know how to register the services when it's using generics inside generics and also how to inject a generic IList or IEnumerable in here.
Attempts
Own attempts
What I tried for the IHandler:
services.AddScoped<IHandler, Handler.Event.EventHandler<EventLevelParser>>();
services.AddScoped<IHandler, Handler.Event.EventHandler<EventCounterParser>>();
services.AddScoped(typeof(IServiceFactory<>), typeof(EventHandlerFactory<>));
Unhandled exception. System.AggregateException: Some services are not able to be constructed (Error while validating the service descriptor 'ServiceType: GenericDITest.Handler.IHandler Lifetime: Scoped ImplementationType: GenericDITest.Handler.Event.EventHa
ndler1[GenericDITest.Parser.Event.EventLevelParser]': Unable to resolve service for type 'GenericDITest.Parser.IParser1[GenericDITest.Parser.Event.EventLevelParser]' while attempting to activate 'GenericDITest.Handler.Event.EventHandler1[GenericDITest.Pa
rser.Event.EventLevelParser]'.) (Error while validating the service descriptor 'ServiceType: GenericDITest.Handler.IHandler Lifetime: Scoped ImplementationType: GenericDITest.Handler.Event.EventHandler1[GenericDITest.Parser.Event.EventCounterParser]': Unab
le to resolve service for type 'GenericDITest.Parser.IParser1[GenericDITest.Parser.Event.EventCounterParser]' while attempting to activate 'GenericDITest.Handler.Event.EventHandler1[GenericDITest.Parser.Event.EventCounterParser]'.)
So I tried to use the type specified registration
services.AddScoped(typeof(IHandler), typeof(Handler.Event.EventHandler<>));
services.AddScoped(typeof(IServiceFactory<>), typeof(EventHandlerFactory<>));
but then I get the error
System.ArgumentException: Cannot instantiate implementation type 'GenericDITest.Handler.Event.EventHandler`1[T]' for service type 'GenericDITest.Handler.IHandler'.
Sadly, I don't know enough about generics to really solve this issue. I don't really understand the error message. Maybe I'm defining the services wrong?
Attempts from other users
Like #user1672994 suggested, I made the following changes
IHandler to IHandler<out T>
EventHandler<T> : IHandler to EventHandler<T> : IHandler<T> EventHandlerFactory<T> : IServiceFactory<IHandler> to EventHandlerFactory<T> : IServiceFactory<IHandler<T>>
public IHandler Create(string key) to public IHandler<T> Create(string key).
Now, I can add the service as services.AddScoped(typeof(IHandler<>), typeof(Handler.Event.EventHandler<>)); which now launches the application without any error, however because of all the generics, I now have to also register my factory like this
services.AddScoped(typeof(IServiceFactory<>), typeof(EventHandlerFactory<>));.
But when I access the factory through var service = provider.GetService(typeof(IServiceFactory<>)); I'm getting a new error. Implementation type 'GenericDITest.Handler.Event.EventHandlerFactory1[T]' can't be converted to service type 'GenericDITest.Service.IServiceFactory1[T]'. But this I don't understand, since it's the same interface?
I also uploaded the project to a file hoster so you can check it out: https://mega.nz/#!jf51UALL!sbVrGSXoTtfzZLzOl9i7wwa-OC5s3xlhuf_RJuorIQs
The EventHandler shall get ANY implementation of IParser
That’s not how generics work though. An IParser<T> is a completely separate type from some IParser. Unless you actually have a type hierarchy between them, they are completely unrelated. Generics can be a bit confusing here because a generic type isn’t actually a type that really exists. It’s only when you apply a concrete generic type argument, that there is a type.
So both IParser<EventLevel> and IParser<EventCounter> are real types (completely separate and unrelated ones though!), but the generic “template” for these types, IParser<T>, is not a concrete type.
You also have to understand that within a generic class, a generic type argument T will always refer to the exact same thing. That is basically a constant type that will be inserted at some point.
So your EventHandlerFactory<T> may have an IEnumerable<IParser<T>> but if you use the factory for example as EventHandlerFactory<EventCounter>, then that type will have an IEnumerable<IParser<EventCounter>>. So that’s also the type it will look for when resolving it via dependency injection. So it cannot get IParser<EventLevel> instances because those are unrelated to the type it is asking for.
An EventHandlerFactory<EventCounter> would only ever inject parsers for EventCounter and not other ones (which may not be what you were trying to do). And instead of using that type to also create handlers for EventLevel, you would have to use a different factory of the type EventHandlerFactory<EventLevel> (which has its own list of parsers for EventLevel).
It’s not entirely clear to me what exactly you are trying to accomplish, especially since your implementations in your example are all empty, but if I were to guess, I would assume that you are just using too many generics here. So starting from the start, these are my assumptions:
You have multiple parsers that can work with different types, but only ever one type per parser.
You have a single event handler type that uses a compatible parser to handle some event of a type.
You want a single factory that allows you to get the right event handler to handle your key.
Now there is a fundamental problem with your approach though: The parsers return a list of a concrete type, e.g. your EventCounterParser returns a list of EventCounter objects. If you now want to use that object within your event handler, then you will need separate event handler types to handle each of those results separately. Otherwise you have no common information to work with within your generic event handler.
What the right solution here is highly depends on what exactly you are trying to do with your parsers and event handlers. With the information we have that’s very difficult to address.
If we were to ignore that part of the parsers though, and the event handlers for example would just utilize the parser in a non-generic way, then you could reduce the setup to the following:
public interface IParser
{
List<object> Parse();
bool SupportsParsing(string key);
}
public class EventHandler : IHandler
{
private readonly IParser _parser;
public EventHandler(IParser parser)
{
_parser = parser;
}
public void Handle()
{ }
}
public class EventHandlerFactory
{
private readonly IEnumerable<IParser> _parsers;
public EventHandlerFactory(IEnumerable<IParser> parsers)
{
_parsers = parsers;
}
public IHandler Create(string key)
{
foreach (var parser in _parsers)
{
if (parser.SupportsParsing(key))
{
return new EventHandler(parser);
}
}
throw new NotImplementedException();
}
}
services.AddScoped<IParser, EventCounterParser>();
services.AddScoped<IParser, EventLevelParser>();
services.AddScoped<EventHandlerFactory>();
Related
I have multiple dependency injected handlers and want to find the appropriate one to solve a task. I finally ended up finding the corresponding handler (in class HandlerResolver) but i can't return the Handler as method result.
Interfaces:
public interface IMessage
{
}
public interface IHandler<TRoot, in TMessage> where TRoot : class
{
public abstract TRoot Apply(TRoot root, TMessage message);
}
// Marker interface to register/resolve dependency injected handlers
public interface IHandler
{
}
Handlers:
public class Handler1: IHandler<MyRoot, Message1>, IHandler
{
public MyRoot Apply(MyRoot aggregateRoot, Message1 message)
{
...
return aggregateRoot;
}
}
public class Handler2: IHandler<MyRoot, Message2>, IHandler
{
public MyRoot Apply(MyRoot aggregateRoot, Message2 message)
{
...
return aggregateRoot;
}
}
Messages:
public class Message1 : ICaseMessage
{
...
}
public class Message2 : ICaseMessage
{
...
}
DI Registrations:
services.AddScoped<IResolver, HandlerResolver>();
services.AddScoped<IHandler, Handler1>();
services.AddScoped<IHandler, Handler2>();
Resolve Handler:
public class HandlerResolver : IResolver
{
private IEnumerable<IHandler> Handlers { get; } // DI injected handlers
public HandlerResolver(IEnumerable<IHandler> handlers)
{
Handlers = handlers;
}
public IHandler<TRoot, TMessage> GetHandler<TRoot, TMessage>(TRoot root, TMessage message)
where TRoot : class
where TMessage : class,
{
var concreteRootType = root.GetType();
var concreteMessageType = message.GetType();
var handlerOrNull = this.Handlers.FirstOrDefault(p =>
{
var genericArguments = p.GetType().GetInterfaces().First().GenericTypeArguments;
return genericArguments[0] == concreteAggregateType &&
genericArguments[1] == concreteMessageType;
});
if (handlerOrNull == null)
{
throw new NotImplementedException($"No Handler Found");
}
else
{
return handlerOrNull as IHandler<TRoot, TMessage>;
}
}
}
return handlerOrNull as IHandler<TRoot, TMessage>;
This will always return null. I think this is due to the parsing. It seems trying to parse it into a IHandler<TRoot, IMessage> which for some reason doesn't work.
I have also tried this solution How to determine if a type implements a specific generic interface type which doesn't work if the generic type is not known.
Typically you should register the actual full type of your objects, i.e.
services.AddScoped<IHandler<MyRoot, Message1>, Handler1>();
that should let you just get the correct service for your types:
services.GetRequiredService<IHandler<MyRoot, Message1>>()
At least I think that is the correct methods to use for Microsoft.Extensions.DependencyInjection, it is not a library I'm familiar with.
Note that you need to register and resolve the actual types. Resolving IHandler<MyRoot, IMessage> will not work, since that would imply that the handlers could take any kind of IMessage-parameter, but they cannot, they can only take Message1/Message2.
Contravariance works in the opposite direction you seem to think, i.e. you would have a Handler1: IHandler<MyRoot, IMessage> that could be converted to a IHandler<MyRoot, Message1>. That works since the handler promises to accept all IMessage implementation, and Message1 is such an implementation. But it might cause trouble with resolving, since resolving typically require exact type matches.
You might get around that by resolving by hand, i.e. get the type object for your interface from the generic type arguments, and traverse the types by hand, checking if they are assignable. But this is not something I would recommend if you are not trying to do anything weird.
var handlerType = typeof(IHandler<TRoot, TMessage>);
Handlers.FirstOrDefault(p => handlerType.IsAssignableFrom(p.GetType()));
The actual handler type should be IHandler<MyRoot, Message1> or IHandler<MyRoot, Message2> depending on the generic type parameters.
I'm having trouble with getting generics and DI to work together in an MVC Core project. I have a generic class (this is just a fragment). I need to initialize Input and Output because of how these are used in other parts of the code, so I'm using Activator to provide initial values and the new() constraint.
public class Message<TIn, TOut> :
where TIn : class, IMessagePart, new()
where TOut : class, IMessagePart, new() {
public Message(){}
public Message(TIn inpart, TOut outpart) {
Input = inpart;
Output = outpart;
}
public TIn Input { get; set; } = (TIn)Activator.CreateInstance(typeof(TIn));
public TOut Output { get; set; } = (TOut)Activator.CreateInstance(typeof(TOut));
}
I have other classes that are used by this, and they have some static utility classes. I'm trying to replace these static classes using DI.
public class Project : IMessagePart{
int y = 1;
var x = StaticUtilityClass.StaticMethod(y);
}
is used like this
var projectMessage = new Message<Project, Project>();
I'm converting the static utility classes to instance classes and injecting them. I'm using the built-in .Net core container. I converted the utilities to instance classes and registered them as concrete singletons in the container. For most things I can just do the normal-
public class SomeClass{
private readonly UtilityClass _utility;
public SomeClass(UtilityClass utility){
_utility = utility;
var x = _utility.Method(1);
}
Things work fine until I get to the generics. I can't do constructor injection on projectMessage, because the generic needs to new it up and it has the new() constraint, so I need a parameterless constructor. If I add just the an injecting constructor I get
'Project' must be a non-abstract type with a public parameterless
constructor in order to use it as parameter 'TIn' in the generic type
or method 'Message'.
and if I add both constructors Activator is only going to call the one without parameters, so DI isn't invoked. I tried using the overload of CreateInstance a few different ways, but no luck tricking it.
Any suggestions here? I don't know if I should stay with statics, try some sort of service locator approach, or if there is a different way to writing the generic.
The answer to why you are getting the error you're seeing is the new() constraints. That specifies that the parameter must have a public parameterless constructor. Which is exactly what your error says. Removing that constraint should fix that error. However, you still have another issue if you want to use DI.
Aside from IMessagePart none of your classes have backing interfaces. In order to use DI effectively you need to define an IMessage, IProject etc. Then your container can create specific instances at runtime, rather than using the Activators like you are now. So your Message declaration would look like:
public class Message<TIn, TOut> : IMessage,
where TIn : class, IMessagePart
where TOut : class, IMessagePart
{
public TIn input { get; set; }
public TOut output { get; set; }
public Message(TIn inpart, TOut outpart) {
this.input = inpart;
this.output = outpart;
}
}
You would setup your DI container something like:
public Startup()
{
var container = new DiContainer(); // I know this is the wrong name; I'm not familiar with the built in container naming and functionality.
container.Register<IMessage>();
container.Register<IMessagePart>();
container.Register<IProject>();
// Register other stuff here
}
Change the syntax there for your specific container. You also have the option of registering your instances something like:
container.Register<Message>(() => Message(part1, part2));
so that you specifically inject a Message that is newed up at Startup time, but that's not really ideal in most cases. Usually you want your DI container to dynamically create an instance as needed (hence the interfaces), rather than using a single concrete instantiation. There are exceptions of course; a SQL connection is one common one.
I'm brand new to using Simple Injector although I have been using Ninject for a long time, so I am comfortable with DI in general. One thing that attracted me to want to use Simple Injector was the ease of use of Decorators.
I have been able to successfully use decorators with Simple Injector in all normal cases where the dependencies are resolved when the service is requested. However, I am having a hard time figuring out if there is a way to get my decorators applied in a case when the service must be constructed using a runtime value.
In Ninject, I could pass a ConstructorArgument to the kernel.Get<IService> request that could be inherited down the chain of N decorators all the way to the "real" implementing class. I cannot figure out a way to replicate that using Simple Injector.
I have put some very basic code below to illustrate. What I would want to do in the real world would be to pass an IMyClassFactory instance into other classes in my application. Those other classes could then use it to create IMyClass instances using the IRuntimeValue they would provide. The IMyClass instance they got from the IMyClassFactory would be decorated automatically by the registered decorators.
I know I could manually apply my decorator(s) in my IMyClassFactory or any Func<IMyClass> I could come up with, but I would like it to "just work".
I keep going around and around trying to abstract out the MyClass construction, but I can't figure out how to get it to resolve with the IRuntimeValue constructor argument and be decorated.
Am I overlooking an obvious solution?
using System;
using SimpleInjector;
using SimpleInjector.Extensions;
public class MyApp
{
[STAThread]
public static void Main()
{
var container = new Container();
container.Register<IMyClassFactory, MyClassFactory>();
container.RegisterDecorator(typeof (IMyClass), typeof (MyClassDecorator));
container.Register<Func<IRuntimeValue, IMyClass>>(
() => r => container.GetInstance<IMyClassFactory>().Create(r));
container.Register<IMyClass>(() => ?????)); // Don't know what to do
container.GetInstance<IMyClass>(); // Expect to get decorated class
}
}
public interface IRuntimeValue
{
}
public interface IMyClass
{
IRuntimeValue RuntimeValue { get; }
}
public interface IMyClassFactory
{
IMyClass Create(IRuntimeValue runtimeValue);
}
public class MyClassFactory : IMyClassFactory
{
public IMyClass Create(IRuntimeValue runtimeValue)
{
return new MyClass(runtimeValue);
}
}
public class MyClass : IMyClass
{
private readonly IRuntimeValue _runtimeValue;
public MyClass(IRuntimeValue runtimeValue)
{
_runtimeValue = runtimeValue;
}
public IRuntimeValue RuntimeValue
{
get
{
return _runtimeValue;
}
}
}
public class MyClassDecorator : IMyClass
{
private readonly IMyClass _inner;
public MyClassDecorator(IMyClass inner)
{
_inner = inner;
}
public IRuntimeValue RuntimeValue
{
get
{
return _inner.RuntimeValue;
}
}
}
Edit 1:
Ok, thanks to Steven for the great answer. It has given me a couple of ideas.
Maybe to make it a little more concrete though (although not my situation, more "classic"). Say I have an ICustomer that I create at runtime by reading a DB or deserializing from disk or something. So I guess that would be considered a "newable" to quote one of the articles Steven linked. I would like to create an instance of ICustomerViewModel so I can display and manipulate my ICustomer. My concrete CustomerViewModel class takes in an ICustomer in its constructor along with another dependency that can be resolved by the container.
So I have an ICustomerViewModelFactory that has a .Create(ICustomer customer) method defined which returns ICustomerViewModel. I could always get this working before I asked this question because in my implementation of ICustomerViewModelFactory I could do this (factory implemented in composition root):
return new CustomerViewModel(customer, container.GetInstance<IDependency>());
My issue was that I wanted my ICustomerViewModel to be decorated by the container and newing it up bypassed that. Now I know how to get around this limitation.
So I guess my follow-up question is: Is my design wrong in the first place? I really feel like the ICustomer should be passed into the constructor of CustomerViewModel because that demonstrates intent that it is required, gets validated, etc. I don't want to add it after the fact.
Simple Injector explicitly lacks support for passing on runtime values through the GetInstance method. Reason for this is that runtime values should not be used when the object graph is constructed. In other words, the constructors of your injectables should not depend on runtime values. There are several problems with doing that. First of all, your injectables might need to live much longer than those runtime values do. But perhaps more importantly, you want to be able to verify and diagnose your container's configuration and that becomes much more troublesome when you start using runtime values in the object graphs.
So in general there are two solutions for this. Either you pass on the runtime value through the method call graph or you create a 'contextual' service that can supply this runtime value when requested.
Passing on the runtime value through the call graph is especially a valid solution when you practice architectures like this and this where you pass on messages through your system or when the runtime value can be an obvious part of the service's contract. In that case it is easy to pass on the runtime value with the message or the method and this runtime value will also pass through any decorator on the way through.
In your case this would mean that the factory creates the IMyService without passing in the IRuntimeValue and your code passes this value on to the IMyService using the method(s) it specifies:
var service = _myServiceFactory.Create();
service.DoYourThing(runtimeValue);
Passing through the runtime value through the call graph however is not always a good solution. Especially when this runtime value should not be part of the contract of the message that is sent. This especially holds for contextual information use as information about the current logged in user, the current system time, etc. You don't want to pass this information through; you just want it to be available. We don't want this, because this would give an extra burden to the consumers of passing the right value every time, while they probably shouldn't even be able to change this information (take the user in who's context the request is executed for instance).
In that case you should define service that can be injected and allows retrieving this context. For instance:
public interface IUserContext {
User CurrentUser { get; }
}
public interface ITimeProvider {
DateTime Now { get; }
}
In these cases the current user and the current time aren't injected directly into a constructor, but instead these services are. The component that needs to access the current user can simply call _userContext.CurrentUser and this will be done after the object is constructed (read: not inside the constructor). Thus: in a lazy fashion.
This does mean however that the IRuntimeValue must be set somewhere before MyClass gets invoked. This probably means you need to set it inside the factory. Here's an example:
var container = new Container();
var context = new RuntimeValueContext();
container.RegisterSingle<RuntimeValueContext>(context);
container.Register<IMyClassFactory, MyClassFactory>();
container.RegisterDecorator(typeof(IMyClass), typeof(MyClassDecorator));
container.Register<IMyClass, MyClass>();
public class RuntimeValueContext {
private ThreadLocal<IRuntimeValue> _runtime;
public IRuntimeValue RuntimeValue {
get { return _runtime.Value; }
set { _runtime.Value = value; }
}
}
public class MyClassFactory : IMyClassFactory {
private readonly Container _container;
private readonly RuntimeValueContext context;
public MyClassFactory(Container container, RuntimeValueContext context) {
_container = container;
_context = context;
}
public IMyClass Create(IRuntimeValue runtimeValue) {
var instance = _container.GetInstance<IMyClass>();
_context.RuntimeValue = runtimeValue;
return instance;
}
}
public class MyClass : IMyClass {
private readonly RuntimeValueContext _context;
public MyClass(RuntimeValueContext context) {
_context = context;
}
public IRuntimeValue RuntimeValue { get { return _context.Value; } }
}
You can also let the MyClass accept the IRuntimeValue and make the following registration:
container.Register<IRuntimeValue>(() => context.Value);
But the disallows verifying the object graph, since Simple Injector will ensure that registrations never return null, but context.Value will be null by default. So another option is to do the following:
container.Register<IMyClass>(() => new MyClass(context.Value));
This allows the IMyClass registration to be verified, but will during verification still create a new MyClass instance that is injected with a null value. If you have a guard clause in the MyClass constructor, this will fail. This registration however disallows MyClass to be auto-wired by the container. Auto-wiring that class can come in handy when you've got more dependencies to inject into MyClass for instance.
I need to have the generic type parameter as an interface, however I would like to instantiate the type in the generic class (SomeGenericType) as follows:
class Program
{
static void Main(string[] args)
{
var val = new SomeGenericType<ISomeInterface>();
Console.ReadKey();
}
}
internal class SomeGenericType<T> where T : new()
{
public SomeGenericType()
{
var test = new T();
}
}
public class SomeClass : ISomeInterface
{
public string TestVal { get; set; }
}
public interface ISomeInterface
{
string TestVal { get; set; }
}
This throws the following compile time error:
"ISomeInterface must be a non-abstract type with a public parameterless constructor in order to use it as parameter 'T' in the generic type or method SomeGenericType"
I understand why it happens, however I was wondering if there is any way around this problem?
Thanks.
No, the new() constraint requires that an instance of the type can be created with the syntax
new T()
This clearly isn't true of either an abstract class or an interface, only a concrete class with a public parameterless constructor.
You could defer the problem until runtime by removing the constraint, and using:
Activator.CreateInstance<T>()
instead to create the object. Then as long as the actual type used at runtime satisfies these constraints, your code will work as you want it to. However, if you do attempt to use an interface or an abstract class, then you will encounter a runtime error.
In your specific case, this line would throw an exception
var val = Activator.CreateInstance<SomeGenericType<ISomeInterface>>();
You're past the compile-time error, but to no effect.
An alternative idea, which may be irrelevant, but it looks like you are looking for a way to ask for an ISomeInterface, and have an instance of its "default" implementation SomeClass provided. This is the sort of thing that an Inversion of Control (IOC) container can handle for you. If you want to investigate further, you could look at Spring.NET, Microsoft Unity, AutoFac, LinFu or one of many other frameworks.
The problem is, there is no way for the compiler to know which class to instantiate for the given interface. As David M points out:
This is the sort of thing that an Inversion of Control (IOC) container can handle for you
I think using a framework might be over kill for this simple requirement. What you can do is create a Factory class of your own like this:
public class Factory
{
Dictionary<Type, Type> typeMapping = new Dictionary<Type, Type>();
public void Register<IType, CType>()
{
typeMapping.Add(typeof(IType),typeof(CType));
}
public IType Create<IType>()
{
Activator.CreateInstance(typeMapping[typeof(IType)]);
}
}
throw in a few sanity checks and this class should be ready to use.
The issue here is the new constraint is tied to having a concrete type implementation. This can't ever work with simply and interface or abstract class since they cannot be directly instantiated. You must provide a concrete class here
var val = new SomeGenericType<SomeClass>()
I am currently designing a class library that will provide data to a web application graph rendering engine in C#. I am currently defining the interfaces of this library.
I have a IGraphData interface which I would like to cache using a service that accesses the cache, this is called IGraphDataCacheService and has set and get methods to add and retrieve IGraphData objects to and from the cache. the cache service will be a singleton.
I am confused about the correct way to implement this, so that there is only one cache service that can get and set generic IgraphData objects.
I came up with this:
interface IGraphDataCacheService {
IGraphData<object> Get(string identifier);
void Set(IGraphData<object> graphData);}
or this:
T Get<T, P>(string identifier) where T : IGraphData<P>;
void Set<T,P>(T graphData) where T : IGraphData<P>;
Can any one offer any advice help?
Thanks
Why don't you just make the interface generic instead?
interface ICacheService<T> {
T Get(string identifier);
void Set(T graphData);
}
if you wanted, you could type-constrain T to be of type IGraphData, or you could write it as:
interface IGraphDataCacheService<T> {
IGraphData<T> Get(string identifier);
void Set(IGraphData<T> graphData);
}
A few points:
I'd probably rename the interface methods to be more emblematic of a caching service. For example, Fetch and Store instead of Get and Set, which makes it sound like you're getting or setting the provider rather than the data to be cached.
Ensuring that there is only one cache is an implementation detail, not an interface one.
To implement a singleton, try something like:
public class SingletonCacheService : IGraphDataCacheService {
private static Singleton instance;
private Singleton() {}
// snip implementation of IGraphDataCacheService methods ...
public static Singleton Instance {
get {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
}
Note that this simple version isn't threadsafe.
Both alternatives seem plausible at a glance; my hunch is that you need to write some 'typical' client code to decide. e.g. Does the typical client 'know' the type of data associated with the identifier it's looking up? Good API design requires identifying the main use scenarios and using that to inform the design.
If I understand your question correctly you are wanting to treat the generic types like they are the same, but in current .NET implementation you can't do this.
IGraphData<string> can't be passed as a IGraphData<object> they are actually different types even though string is an object, the generic types are not related and can't be cast or passed like they are the same.
If you control the IGraphData interface you can create a IGraphData interface and derive IGraphData from it and use IGraphData to access the cache. It just depends on how you are using it and what you have the control over.
You can do what you want in C# 4.0. There is an article about it here
You can't ensure there's only a single instance implementing an interface. However, you can make a class (e.g. GraphDataCacheServiceImpl) implementing the interface a singleton by sealing it and providing only a getter property, with the object created as a static variable from a private constructor. See the below. As far as generics, it's not exactly clear what you're seeking to accomplish. But I would guess the below is close to what you want.
interface IGraphDataCacheService<T> {
IGraphData<T> Get(string identifier);
void Set(IGraphData<T> graphData);
}
public sealed class GraphDataCacheServiceImpl<T> : IGraphDataCacheService<T>
{
private GraphDataCacheServiceImpl()
{
// ..
}
static GraphDataCacheServiceImpl()
{
Instance = new GraphDataCacheServiceImpl<T>();
}
public IGraphData<T> Get(string id)
{
return new GraphDataImpl<T>();
}
public void Set(IGraphData<T> graphData)
{
}
public static GraphDataCacheServiceImpl<T> Instance {get; private set;}
}