Generic types - type parameter madness - c#

Szenario
I am currently encountering the following challenge in a solution that needs high extensibility and I am therefore using many abstract class templates.
There is a service base Service that implements the interface IService.
There are many concrete interfaces IConcreteService that inherit from IService
There are many concrete services ConcreteService that inherit from Service and implement IConcreteService.
Now consider a method like public Result Get(Input input) whereas both Result and Input are abstract base classes and like the Services implement many concrete types.
That results in
interface IService<TResult, TInput>
where TResult : Result
where TInput : Input
{
public TResult Get(TInput input);
}
class Service<TResult, TInput> : IService<TResult, TInput>
where TResult : Result
where TInput : Input
{ ... }
interface IConcreteService : IService<ConcreteResult, ConcreteInput> { ... }
class ConcreteService : Service<ConcreteResult, ConcreteInput>, IConcreteService { ... }
So far so good
But now Input has some generic types as properties
abstract class Input<TPropType> where TPropType : PropType
{
public TPropType Prop { get; set; }
}
class ConcreteInput : Input<ConcretePropType> { ... }
To use the generic input I have to make the following changes
interface IService<TResult, TInput, TPropType> // Add template parameter
where TResult : Result
where TInput : Input<TPropType> // Change existing constraint
where TPropType : PropType // Add constraint for new paramter
{
public TResult Get(TInput input)
}
abstract class Service<TResult, TInput, TPropType> : IService<TResult, TInput, TPropType>
where TResult : Result
where TInput : Input<TPropType>
where TPropType : PropType
{ ... }
interface IConcreteService : IService<ConcreteResult, ConcreteInput, ConcretePropType>
{ ... }
class ConcreteService : Service<ConcreteResult, ConcreteInput, ConcretePropType>, IConcreteService
{ ... }
Question
As you can imagine, with each generic property type, the list of template parameters is getting longer and longer on my services. So my question is, if there is any way or best practice to avoid this before it is getting out of hand?
Also consider that a concrete implementation follows a vertical use case, so use case A has a
UseCaseAInput with a UseCaseAPropType
UseCaseAResult
UseCaseAService

Related

Generic type constraints - can I extend class constraint on method level?

I am working on some kind of a fluent API which would easily allow to register all necessary classes in the IoC container. For example:
builder
.WithInput<EncryptedMessage>()
.UseEncryptedMessageParser()
// ...
// .OtherEncryptedMessageFlowRegistrations()
builder
.WithInput<StandardMessage>()
.UseStandardMessageParser()
// ...
// .OtherStandardMessageFlowRegistrations()
What I'd like to achieve is to have less strict generic type constraints at the beginning, but make it more strict later in the hierarchy. Therefore, I am wondering if it's somehow possible to create a class with generic type constraints and then extend this generic type with additional constraints on method level?
public class A<T> where T : class
{
public void Foo(T input) where T : SomeBaseClass // additional constraints for generic type on class level
{
}
}
More realistic example:
public class MessageFlowBuilder<TInput> where TInput : class, new()
{
private readonly IServiceCollection _services;
public MessageFlowBuilder(IServiceCollection services)
{
_services = services;
}
public StandardMessageFlowBuilder<TInput> UseStandardMessageParser()
where TInput : StandardMessageBase // additional constraints for generic type on class level (required by parser)
{
_services.AddTransient<StandardMessageParser<TInput>>();
return new StandardMessageFlowBuilder<TInput>();
}
public EncryptedMessageFlowBuilder<TInput> UseEncryptedMessageParser()
where TInput : EncryptedMessageBase // additional constraints for generic type on class level
{
_services.AddTransient<EncryptedMessageParser<TInput>>();
return new EncryptedMessageFlowBuilder<TInput>();
}
}
Or maybe is it possible to create another generic type on method level which then would have a constraint to be of the same type as class level argument?
public class A<T> where T : class
{
public void Foo<T2>(T2 input) where T2 : SomeBaseClass // + some T == T2 type equality?
{
}
}
Could could make Foo a generic method, yes - but you can't constraint it to be the same as T, and you can't add a constraint to an existing type parameter.
Could you try something like this? I am not even sure right now whether this will compile fine or not. The method needs to be generic too on its own.
public class A<T> where T : class
{
public void Foo<U>(U input) where U : T, SomeBaseClass, SomeInterface //whatever
{
}
}
public class A<T> where T : class
{
public void Foo(T input) where T : SomeBaseClass // additional constraints for generic type on class level
{
}
}
No, the type T is declared when you instantiate the class, you can't change that at method level.
So by doing:
new A<SomeBase>();
You have effectively given the method the signature:
public void Foo(SomeBase input)

Type parameter declaration must be an identifier

interface IRepository<TEntity,Tid> where TEntity :class
{
int Insert(TEntity entity);
int Delete(TEntity entity);
TEntity GetById(Tid id);
TEntity GetByFilters(TEntity entity);
int Update(TEntity entity);
}
Trying to implement
internal class Repository<XYZClass, string> : IRepository<XYZCLass, string>
{
//...All interface methods
}
Getting below error:
type parameter declaration must be an identifier not a type
Any Suggestions..
The declaration should only include type parameters for those that are generic to those consuming the class.
In other words, if you implementation of IRepository sets the TEntity parameter to XYZClass and the TID parameter to string (ie a concrete class definition), then you should have:
internal class Repository : IRepository<XYZCLass, string>
{
//...All interface methods
}
If you wish to define one type and leave the other generic then:
internal class Repository<TEntity> : IRepository<TEntity, string>
{
//...All interface methods
}
Otherwise, both types should still be left open:
internal class Repository<TEntity,TID> : IRepository<TEntity, TID>
{
//...All interface methods
}
If you want Repository to be non-generic you need to specify both types:
internal class Repository : IRepository<XYZClass, string> { ... }
If you want Repository to be generic but specify that TId is a string you can use
internal class Repository<TEntity> : IRepository<TEntity, string> where TEntity : class
{
//...All interface methods
}
string is a type, not a generic type parameter.
Maybe you want a base class constraint?
internal class Repository<TYourEntity> : IRepository<TYourEntity, string>
where TYourEntity : XyzClass
{
//...All interface methods
}
Note that this constraint means that the constraint on the interface (the reference type constraint TEntity : class) is satisfied.
For completeness, the other "legal" interpretations of what you meant (also in other answers already), are:
// don't constrain generic parameter, just name it TXyz (TXyz is "declared" here)
internal class Repository<TXyz> : IRepository<TXyz, string>
where TXyz : class
{
//...All interface methods
}
and:
// make class non-generic (the type XyzClass exists and is declared elsewhere)
internal class Repository : IRepository<XyzClass, string>
{
//...All interface methods
}
Thus repository is not generic (it is implementation of repository interface parametrized with concrete classes):
internal class Repository : IRepository<XYZCLass, string>

Hiding type parameters in implementation of interface

Given the following Interface:
public interface IContext {
TOutput get<TInput, TOutput>(TInput command);
}
And the following implementation:
public class DbContext: IContext {}
public class Repository {
private readonly IContext _context;
public Repository(IContext context) {...}
public IDto get(int id) {
var data = _context.get<IThis, IThat>(id)
//map data to dto and return
}
}
Since I'm passing the IContext dependency into the Repository class I don't want type parameters on the interface since it would lock me into using an explicit implementation of IContext. Kind of muting the point of interfaces right?
Given my constraints of not having type parameters on the IContext interface, how can I implement IContext so that I can call _context.get(...) in my Repository class instead of _context.get<IThis, IThat>(...)?
So in other words, I'd like the implementation of IContext (In this example DBContext) to define the type parameters for get() so that when the method is actually invoked the invoker doesn't need to know anything about the type parameters to be passed.
Update
The problem I'm trying to fix is allowing any kind of IContext to be passed into a repository. If I have class level type parameters on the interface then I'm restricted to only implementations of IContext<TThis, TThat>, which isn't ideal.
If it's important that an IContext object have a get function that can accept any object as the input and provide any object as the output then rather than generics, your interface method should simply use object:
public interface IContext {
object get(object command);
}
(Implementors of the interface may then have a strongly typed method exposed, and use explicit interface implementation for that method.
The other possibility is that your IContext object knows, when accepting the IContext object, what the signature of the method should be. In that case, you'd want to make the interface, and not its method, generic:
public interface IContext<TInput, TOutput> {
TOutput get(TInput command);
}
This of course means that you couldn't have, say, a collection of objects of all sorts of different kinds of IContext objects; they'd need to all share a signature. Methods accepting or returning an IContext object would need to either know what type the input/output should be at compile time, or would themselves need to be generic.
On a side note that may or may not be relevant to your use of the interface, you can leverage covariance and contravariance on your generic arguments, which may or may not aid in your use of the interface:
public interface IContext<in TInput, out TOutput>
{
TOutput get(TInput command);
}
Without understanding how you expect the interface to actually be used it's unclear which would be the better solution.
As I understand, you require concrete typing together with loose coupling through interface.
Consider this:
public interface ICommand {}
public interface IContextOutput {}
public interface IContext
{
IContextOutput Get(ICommand input);
}
public abstract class ContextBase<TInput, TOutput> : IContext
where TInput : ICommand
where TOutput : IContextOutput
{
IContextOutput IContext.Get (ICommand input)
{
if (input.GetType () != typeof(TInput))
{
throw new ArgumentOutOfRangeException("input");
}
return Get((TInput)input);
}
protected abstract TOutput Get(TInput command);
}
And then derive all the contexts from the abstract class.

Generic Constraint to allow casting from interface to implementation

I have a small class that implements a dictionary that maps from the type of an interface to an implementation of that interface that extends from a base class. Unfortunately the abstract base class does not implement the interfaces, so once in the dictionary, there seems to be no way to associate the two. There is another method in this class that is dependent on storing the objects as BaseClass (in fact, most of my class is dependent on that--the getter into the dictionary is somewhat of a convenience).
private readonly Dictionary<Type, BaseClass> dictionary;
public void Add<T>(BaseClass base)
{
if (!(base is T)) // How to get rid of this check?
{
throw new ArgumentException("base does not implement " + typeof(T).Name);
}
this.dictionary.Add(typeof(T), base);
}
public T Get<T>()
{
BaseClass base;
this.dictionary.TryGetValue(typeof(T), out base);
return (T)(object)base; // How to get rid of (object) cast?
}
Are there any clever constraints I can use to remove the (base is T) check, the cast to object, or both?
Here is the class setup, for reference:
class BaseClass { }
interface IThing { }
class MyClass : BaseClass, IThing { }
dict.Add<IThing>(new MyClass());
IThing myClass = dict.Get<IThing>();
The only way to get the compile-time enforcement you're looking for would be if you have compile-type knowledge of the derived type being added.
For example, if you also specify a type parameter for the class being added then you could constrain that the class implement the interface type parameter:
public void Add<TInterface, TClass>(TClass #base)
where TClass : BaseClass, TInterface {
this.dictionary.Add(typeof(TInterface), #base);
}
So you could do this:
MyClass ok = new MyClass();
dict.Add<IThing, MyClass>(ok);
But not this:
class MyClassNotIThing : BaseClass { }
MyClassNotIThing notOk = new MyClassNotIThing();
dict.Add<IThing, MyClassNotIThing>(notOk);
Aside from that, generic constraints don't offer a means by which to constrain that a known type (i.e. BaseClass) inherit from a generic type parameter.
Here is the solution I ended up using. There are a few tricks that can make the Add() safe without the check (see the link in a comment to cokeman19's answer), but I opted not to do that as I find this code a bit cleaner.
interface IThing { }
abstract class BaseClass
{
internal T AsInterface<T> where T : class
{
return this as T;
}
}
class MyClass : BaseClass, IThing { }
class DictionaryClass
{
private readonly Dictionary<Type, BaseClass> dictionary;
public void Add<T>(BaseClass base)
{
if (base is T)
{
dictionary.Add(typeof(T), base);
}
}
public T Get<T>() where T : class
{
return dictionary[typeof(T)].AsInterface<T>();
}
}
Note that this solution does allow calls like:
myClass.AsInterface<IThingItDoesntImplement>()
but this returns null and I made the function internal to prevent strange uses anyway.

Casting generic interface

I have the following .
public interface IMyService<T>
where T: BaseModelType
{
Process(T input);
}
public class BaseModelType
{
...some property
}
public class SomeClass : BaseModelType
{
...some properties
}
public ServiceImpl : IMyService<SomeClass>
{
...the properties
}
Then I have a unity container where i register all the implementations of the generic interface. I want to be able to use the unitycontainer's resolve method to get the interface, then do some work on it. At the time when i want to use the Resolve method i have the type in runtime
new UnityContainer.Resolve(myTypeVar)
Can I somehow cast this to be
IMyService<BaseModelType> value = new UnityContainer.Resolve(myTypeVar) //want to cast it here from object.
So that i can call the Process method that the interface defines.
No, because IMyService<SomeClass> does not implement IMyService<BaseModelType>. If you look at the implementation of the Process method:
public void Process(SomeClass input){...}
This clearly assumes that you're giving it a SomeClass. It should be able to safely access any members of SomeClass. But if you called this method with a BaseModelType as the parameter, that wouldn't work, would it?
Assuming that you know at runtime that your input argument is going to be of the right type for the given generic IMyService<T> interface, you have two options:
Invoke the generic method signature via reflection. A little slow, but effective.
Add a non-generic parent interface for IMyService, which takes a BaseModelType. In your service implementations, you can implement this method by casting the input to the expected type for that implementation. This requires more code. But you could alleviate that somewhat by having a generic abstract base class that implements this method so the other implementations don't have to.
void Main()
{
var s = (IMyService)new ServiceImpl();
s.Process(new SomeClass());
}
public interface IMyService
{
void Process(BaseModelType input);
}
public interface IMyService<in T> : IMyService
where T: BaseModelType
{
void Process(T input);
}
public class BaseModelType{}
public class SomeClass : BaseModelType{}
public abstract class ServiceBase<T> : IMyService<T>
where T: BaseModelType
{
void IMyService.Process(BaseModelType input)
{
Process((T)input);
}
public abstract void Process(T input);
}
public class ServiceImpl : ServiceBase<SomeClass>{
public override void Process(SomeClass input){}
}

Categories

Resources