Generics: can an interface be interogated to find T - c#

I have completely re-written this hoping to make my question clearer. I have chosen the concept of services making use of repositories in my example code.
Example code:
class Program
{
interface IEntity
{
int Id { get; set; }
}
// Example entity could be:
class Book : IEntity
{
public int Id { get; set; }
}
class Magazine : IEntity
{
public int Id { get; set; }
}
interface IRepository<TEntity> where TEntity : class, IEntity
{
IEnumerable<TEntity> GetEntities();
}
interface IBooksRepository : IRepository<Book> { }
interface IMagazineRepository : IRepository<Magazine> { }
class DataStore<TEntity> where TEntity: class, IEntity
{
public IEnumerable<TEntity> GetFromStore()
{
throw new NotImplementedException();
}
}
abstract class RepositoryBase<TEntity> : IRepository<TEntity>
where TEntity : class, IEntity
{
DataStore<TEntity> _dataStore;
public RepositoryBase()
{
_dataStore = new DataStore<TEntity>();
}
public IEnumerable<TEntity> GetEntities()
{
return _dataStore.GetFromStore();
}
}
class BookRepository : RepositoryBase<Book>, IBooksRepository { }
class MagazineRepository : RepositoryBase<Magazine>, IMagazineRepository { }
abstract class ServiceBase<IEntityRepository, TEntity>
where IEntityRepository : IRepository<TEntity>
where TEntity : class, IEntity
{
IEntityRepository _repository;
public ServiceBase(IEntityRepository repository)
{
_repository = repository;
}
public IEnumerable<TEntity> GetEntitiesFromRepository()
{
return new List<TEntity>();
}
}
class BookService : ServiceBase<IBooksRepository, Book>
{
public BookService(IBooksRepository bookRepository)
: base(bookRepository)
{ }
}
class MagazineService : ServiceBase<IMagazineRepository, Magazine>
{
public MagazineService(IMagazineRepository magazineRepository)
: base(magazineRepository)
{ }
}
static void Main(string[] args)
{
var aBookService = new BookService(new BookRepository());
var aMagazineService = new MagazineService(new MagazineRepository());
var books = aBookService.GetEntitiesFromRepository();
var magazines = aMagazineService.GetEntitiesFromRepository();
}
}
This all works fine and perhaps it is valid to ask why I want to change this. Mainly I am just curious if I can make this more neat. It is more a point of curiosity that one of functional correctness I suppose.
Both IBookRepository and IMagazineRepository know which concreate type they represent 'Book' and 'Magazine'
When I define my concreate services: BookService and MagazineService I have to specify the type as well as the interface:
class BookService : ServiceBase<IBooksRepository, Book>{}
class MagazineService : ServiceBase<IMagazineRepository, Magazine>{}
I wondered if I could simplify thier signatures as the Interfaces already know The type I am expecting Book or Magazine.
Can I extract the Entity Type from the inteface such that I no longer need to specify the type when creating concreate service types?
As I pondered this, I discovered a deeper issue with my knowledge of C#:
What exactly is the type of 'thing' that the generic system is looking for between those angle brackets: IEnumerable<TThisThing>.
When I look at intellisense is says T is the type of objects to enumerate.
So as an experiment I grabbed the type of MyType:
Type typeOfMyType = instanceOfMyType.GetType();
IEnumerable<typeOfMyType> enumerable = new List<typeOfMyType>(); //crude example.
Now of course this does not work. So what kind of thing is TThisThing that works between the angle brackets?

is there a way of extracting this information so that I can forgo the
inclusion of 'MyType' in the class definition and use the discovered
TMyType in the example method?
Yes, you simply need to define the generic type parameter in the methods name:
public IEnumerable<TMyType> GetMyTypes<TMyType>()
{
// get list of TMyType instances;
return list;
}
If you don't want to use a generic type parameter at all, you'll have to defer to reflection, and you won't be able to use a compile-time generic type such as returning an IEnumerable<T>.
So what kind of thing is TThisThing that works between the angle
brackets?
TThisThing should be a compile-time known type parameter. When you use Type typeOfMyType = instanceOfMyType.GetType();, the type of instanceOfMyType is only known at run-time.
For example:
var obj = new SomeClass<Foo>();
IEnumerable<Bar> bars = obj.GetMyTypes<Bar>();
Where Foo and Bar:
public class Foo { }
public class Bar { }

Related

C#: generic method constraint on subclassed types

I have two kinds of base classes:
public class Parent { }
public abstract class Child : Parent
{
string ChildKey { get; set; }
}
Derived from Parent, there are many kids:
public class Kid1 : Parent { public string Name { get; set; } }
public class Kid2 : Parent { public long Number { get; set; } }
...
and also many Children as a special group of Childs with extra properties:
public class Child1 : Child { public string Street { get; set; } }
public class Child2 : Child { public long Account { get; set; }}
Now I have two generic repository classes where the "Special One" acts more specific on the extra properties by using an additional filter:
public class Repository<T> : IRepository<T> where T : Parent
{
public IEnumerable<T> GetAll() { return something; }
}
public class ChildRepository<T> : Repository<T>, IChildrenRepository<T> where T : Child
{
public override IEnumerable<T> GetAll() { return base.GetAll().Where(x => x.ChildKey == "y"); }
}
with the interfaces:
public interface IRepository<T> where T : Parent
{ IEnumerable<T> GetAll(); }
public interface IChildRepository<T> : IRepository<T> where T : Child { }
I also need the type safety of the GetAll()-results.
Now I need a generic method to create the desired repository:
IRepository<T> GetRepository() where T : WhatConstraint
{
if (typeof(Child).IsAssignableFrom(T))
return new ChildRepository<T>(); // return 1
return new Repository<T>(); // return 2
}
What is the correct constraint? return 1 needs Child-Constraint (which is wrong for return 2), saying that Type T cannot be used as type parameter in method since there is no implicit reference conversion from T to Child.
The T : Child-constraint is more precise in ChildRepository (and therefore useful, since I can rely on some properties). If I use the same T : Parent-constraint of the Repository, I have to type-check whether T is derived from Child all the times...
Are there any solutions to this?
Okay, here is a detailed solution (which can be written shorter as well as less readable). Since Repository and ChildRepository have conflicting constraints (which is good for the repositories, but bad for GetRepository-factory), I cannot create the ChildRepository using new-keyword. I have to create this object via CreateInstance.
IRepository<T> GetRepository() where T : Parent
{
if (typeof(Child).IsAssignableFrom(T))
{
Type childType = typeof(T); // which is both, Parent and Child
Type classType = typeof(ChildRepository<>);
Type[] typeParams = { childType };
Type repositoryType = classType.MakeGenericType(typeParams);
return Activator.CreateInstance(resultType) as IRepository<T>;
}
return new Repository<T>();
}
Downside of this solution: More complex code analysis, unclear nullability of result, not really intuitive readable (especially existing constraints). But it works.

C# Passing current generic class instance to other class

lately I started to learn generics. I run into trouble with storing references to generic classes instances. As you can see, my class ListHandler can store references to specific type of BaseClass. I would love to register BaseClass instances by themselves, which is why I wanted to guarantee that they will use BaseParamClass by adding 'where'. Anyway - it does not compile.'This', does not know that T is actually BaseClassParam even with 'where' keyword in class. I don't know what is wrong here and I couldn't find answer anywhere. I would be grateful for tips/guides/solutions.
public class ListHandler
{
private List<BaseClass<BaseParamClass>> list;
public ListHandler()
{
list = new List<BaseClass<BaseParamClass>>();
}
public void Register(BaseClass<BaseParamClass> param)
{
list.Add(param);
}
}
public class BaseClass<T> where T : BaseParamClass
{
private ListHandler listHandler;
public T Param { get; private set; }
public BaseClass(ListHandler listHandler)
{
this.listHandler = listHandler;
listHandler.Register(this); //throws error
}
}
Why don't you make ListHandler generic as well?
public class ListHandler<T>
{
private List<BaseClass<T>> list;
public ListHandler()
{
list = new List<BaseClass<T>>();
}
public void Register(BaseClass<T> param)
{
list.Add(param);
}
}
public class BaseClass<T>
{
private ListHandler<T> listHandler;
public T Param { get; private set; }
public BaseClass(ListHandler<T> listHandler)
{
this.listHandler = listHandler;
listHandler.Register(this);
}
}
Also, it seems strange to me to have BaseClass<T> contain a reference to a class that has a reference to BaseClass<T> itself.
I have another option for you.
Let's split the BaseClass<T> class into two with a non-generic base, like so:
public class BaseClass
{
protected ListHandler listHandler;
public BaseClass(ListHandler listHandler)
{
this.listHandler = listHandler;
}
}
public class BaseClass<T> : BaseClass where T : BaseParamClass
{
public T Param { get; private set; }
public BaseClass(ListHandler listHandler)
: base(listHandler)
{
listHandler.Register(this); // Compiles nicely! Yay!
}
}
Now, the list inside ListHandler can be defined as private List<BaseClass> list;. That means there is no problem adding any BaseClass item to the list. We also can then define two methods for registering and fetching generic versions of the BaseClass<T> from the ListHandler. It would look like this:
public class ListHandler
{
private List<BaseClass> list;
public ListHandler()
{
list = new List<BaseClass>();
}
public void Register<T>(BaseClass<T> param) where T : BaseParamClass
{
list.Add(param);
}
public BaseClass<T> Fetch<T>() where T : BaseParamClass
{
return list.Select(x => x as BaseClass<T>).Where(x => x != null).FirstOrDefault();
}
}
So, given a class public class FooParam : BaseParamClass { } I can write this code:
ListHandler listHandler = new ListHandler();
BaseClass<FooParam> baseClass = new BaseClass<FooParam>(listHandler);
BaseClass<FooParam> baseClass2 = listHandler.Fetch<FooParam>();
Console.WriteLine(object.ReferenceEquals(baseClass, baseClass2));
The result from this code is True is written to the console - which means I can successfully fetch the instance of BaseClass<FooParam> from the ListHandler.
Why your code doesn't compile
In order to fully understand why your code doesn't compile, you'll have to dive into covariance and contravariance, which is a big topic and hard to explain in an SO answer. It can be especially confusing if you've gotten to a point where inheritance polymorphism is second nature to you; the rules are just different enough to be make your head hurt.
Here is what is confusing--
You're used to doing this:
object a = new String(...);
But generics don't let you do this!
List<object> c = new List<string>(); //Compiler error
That's because those two Lists are not related the same way that object and string are related. One does not inherit from the other. Rather, they are different variants of a generic type definition. In the generic world, you can't assign one to the other. The same is true of this:
void Foo<T>() where T: BaseParamClass
{
BaseClass<BaseParamClass> a = new BaseClass<T>(); //Compiler error
}
In this example, T could be BaseParamClass or one of its derived types. They are not the same type. So to remain type-safe, the compiler has to disallow this assignment, and your Register call, which has the same type mismatch.
Standard ways around this
You need a covariant interface. These allow assignment from derived to base. So for example, while this is still illegal:
List<object> a = new List<string>(); //Compiler error
This is totally fine:
IEnumerable<object> e = new List<string>(); //Is OK
Because IEnumerable was declared to be covariant, like this:
interface IEnumerable<out T>
Which means it is can be assigned in this way. It works because using out also adds a compiler constraint to the interface: it can be used to retrieve stuff...
interface IEnumerable<out T>
{
T Item[int index];
}
...but it cannot accept anything:
interface IEnumerable<out T>
{
Add(T item); //Compiler error
}
These constraints are what allow generics to provide early-bound type safety while still allowing certain forms of (non-inheritance) polymorphism.
What I'd suggest
Based on your comment, it sounds like you just need a container (a stack, apparently) that can hold references to these BaseClass<T> instances. If you are following separation of concerns, the stack doesn't need to actually do anything with the T, other than store it and retrieve it, and to allow it to register itself.
Since that is a separate concern, make a separate interface.
And in the interest of keeping things simple, maybe avoid using generics completely for this bit.
One way to do it--
Create an interface that allows access to everything the stack needs to know about an item it is containing. For example, if the stack contains popups of various kinds, you may want to expose the popup's title.
interface IStackable
{
string Title { get; set; }
}
Now use it like this:
public class ListHandler
{
private readonly Dictionary<string, IStackable> list;
public ListHandler()
{
list = new Dictionary<string, IStackable>();
}
public void Register(IStackable item)
{
list.Add(item.Title, item);
}
}
public class BaseClass<T> : IStackable where T : BaseParamClass
{
private ListHandler listHandler;
public T Param { get; private set; }
public BaseClass(ListHandler listHandler)
{
this.listHandler = listHandler;
listHandler.Register(this);
}
public string Title { get; set; }
}
Unless there is some other requirement, you shouldn't need to make it any more complicated than that.
All you really need to do is add an interface. This works:
public class BaseParamClass
{
}
public class ListHandler
{
private List<IBase<BaseParamClass>> list;
public ListHandler()
{
list = new List<IBase<BaseParamClass>>();
}
public void Register(IBase<BaseParamClass> param)
{
list.Add(param);
}
}
public interface IBase<T> where T : BaseParamClass
{
T Param {get; }
}
public class BaseClass : IBase<BaseParamClass>
{
private ListHandler listHandler;
public BaseParamClass Param { get; private set; }
public BaseClass(ListHandler listHandler)
{
this.listHandler = listHandler;
listHandler.Register(this);
}
}
Working code on DotNetFiddle

Reducing generic parameters

I'm trying to get an inheritance hierarchy of generics working, and I'm running into a bit of a problem.
Here's an example:
interface IFoo {}
interface IFoo<T> : IFoo
{
T Data { get; }
}
class Foo : IFoo<int> { public int Data { get; set; } }
interface IBar {}
class Bar : IBar { }
abstract class LayerOne<T_FOO, T_BAR> where T_FOO : IFoo where T_BAR : IBar {}
abstract class LayerTwo<T_FOO> : LayerOne<T_FOO, Bar> where T_FOO : IFoo, new()
{
protected T_FOO _foo = new T_FOO();
public void Test1() { _foo.Data.Dump();} // Compiler error
}
class LayerThree : LayerTwo<Foo>
{
public void Test2() { _foo.Data.Dump();}
}
I'm trying to get access to .Data in the LayerTwo class. Clearly, since IFoo doesn't have that property, it's going to error. However, if I change they type of T_FOO to IFoo<T>, then I have to define it and LayerThree as:
abstract class LayerTwo<T_FOO, T> : LayerOne<T_FOO, Bar> where T_FOO : IFoo<T>, new()
{
protected T_FOO _foo = new T_FOO();
public void Test1() { _foo.Data.Dump();}
}
class LayerThree : LayerTwo<Foo, int>
{
public void Test2() { _foo.Data.Dump();}
}
But the intent of the concrete Foo implementation is that it already knows it's implementing IFoo<int>. Is there any way I can get LayerTwo to know about the Data property without requiring it to be looked up from Foo and added to LayerThree's definition?
What I'd love is:
class LayerThree : LayerTwo<Foo> // Automatically realizes that the second generic is int
{
public void Test2() { _foo.Data.Dump();}
}
Update: As it turns out, I was actually trying to implement two contradictory things in my code. The actual LayerTwo was trying to keep T_FOO generic, but also created an (abstract) method which required a specific type from IFoo<T>.
So the solution I'm going with is just to use an interface which inherits from IFoo<T> and specifies the type, but I'm accepting Ondrej Tucny's answer, since it did solve the problem I asked about.
This is exactly the same situation as with IEnumerable / IEnumerable<T>. Your weakly typed interface IFoo needs to provide a weakly typed Data:
public interface IFoo
{
object Data { get; }
}
public interface IFoo<T> : IFoo
{
new T Data { get; }
}
then in the implementation one of the will be explicit to favor strongly typed access:
public class IntFoo : IFoo<int>
{
public int Data { get { return -1; } }
object IFoo.Data { get { return Data; } }
}
Now you have access to Data in either case, although when the actual type T is unknown, you have to live with an object.

Generic Type Parameter Inference

I've got a handy collection in my middle tier which is for collections of child things that belong to a parent thing.
public class ChildCollection<TParent, TChild>
{
public IEnumerable<TChild> GetChildren();
etc.
}
In the interface, I've got a handy grid that can display the contents of a ChildCollection<TParent,TChild> and let users do work on it.
public abstract class ChildCollectionGrid<TCollection, TParent, TChild> : MyGridControl
where TCollection : ChildCollection<TParent, TChild>
{
public abstract TCollection Collection;
etc.
}
Inheriting this class to make a grid to work with the Waffles on a Widget ends up looking like this.
public class WidgetWafflesGrid : ChildCollectionGrid<WidgetWafflesCollection, Widget, Waffle>
This is a little redundant. A WidgetWaffleCollection is a ChildCollection<Widget,Waffle>. With that first generic type argument specified, the class won't compile unless you specify exactly those two others.
Is there a prettier way to accomplish this where the compiler could infer those other two types? I know I'm being finicky but ideally I would like to have the class declaration look like:
public class WidgetWafflesGrid : ChildCollectionGrid<WidgetWafflesCollection>
Thanks for your help!
No, there's not. Generic parameter inference works only on methods.
Why derive from your collection? Just keep it like:
public abstract class ChildCollectionGrid<TParent, TChild> : MyGridControl
{
public abstract ChildCollection<TParent, TChild> Collection;
etc.
}
public class WidgetWafflesGrid : ChildCollectionGrid<Widget, Waffle>
{
}
The only way to handle inheritance in collections with Generics is using the Collection<TCollection,TChild> : where TCollection : Collection<TCollection,TChild> { } pattern.
Here is an example with a concrete class
public abstract class Collection<TCollection, TChild>
where TCollection : Collection<TCollection, TChild>, new()
{
protected Collection()
{
List=new List<TChild>();
}
protected List<TChild> List { get; set; }
public TCollection Where(Func<TChild, bool> predicate)
{
var result=new TCollection();
result.List.AddRange(List.Where(predicate));
return result;
}
public void Add(TChild item) { List.Add(item); }
public void AddRange(IEnumerable<TChild> collection) { List.AddRange(collection); }
}
public class Waffle
{
public double Temperature { get; set; }
}
public class WafflesCollection : Collection<WafflesCollection, Waffle>
{
public WafflesCollection BurnedWaffles
{
get
{
return Where((w) => w.Temperature>108);
}
}
}
class Program
{
static void Main(string[] args)
{
WafflesCollection waffles=new WafflesCollection();
// Count = 3
waffles.Add(new Waffle() { Temperature=100 });
waffles.Add(new Waffle() { Temperature=120 });
waffles.Add(new Waffle() { Temperature=105 });
var burned=waffles.BurnedWaffles;
// Count = 1
}
}

Generic method with generic type problem

I have problem with constraints on generic method. Here is code for all classes:
namespace Sdk.BusinessObjects
{
public interface IBusinessObject
{
}
}
namespace Sdk.BusinessObjects
{
[DataContract]
public class AccountDetails : IBusinessObject
{
[DataMember]
public virtual Guid AccountId { get; set; }
// More properties...
}
}
namespace Sdk.BusinessLogic
{
public interface IManager<T> where T : IBusinessObject
{
T Add(T businessObject);
void Delete(T businessObject);
IList<T> ListAll();
}
}
namespace Sdk.BusinessLogic
{
public interface IAccountManager : IManager<AccountDetails>
{
void ChangeAccountState(Guid accountId, string state);
}
}
namespace Sdk.BusinessLogic
{
public interface IManagerFactory
{
T Create<T>() where T : IManager<IBusinessObject>;
}
public class ManagerFactory : IManagerFactory
{
public T Create<T>() where T : IManager<IBusinessObject>
{
// resolve with Unity and return
}
}
}
So, I have main IBusinessObject interface for all business objects (like AccountDetails) and IManager as generic manager interface for business objects. I wanted to create factory for these managers with constraints. When I try something like this in UnitTest:
IManagerFactory factory = new ManagerFactory();
factory.Create<IAccountManager>();
I get error:
The type 'Sdk.BusinessLogic.IAccountManager' cannot be used as type parameter 'T' in the generic type or method 'Sdk.BusinessLogic.IManagerFactory.Create()'. There is no implicit reference conversion from 'Sdk.BusinessLogic.IAccountManager' to 'Sdk.BusinessLogic.IManager'.
How can this be done?
Basically your problem is that IManager<T> is invariant, and has to be as you've got values coming out of the API and values going into it. So an IAccountManager isn't an IManager<IBusinessObject>, because otherwise you could write:
IAccountManager m1 = new SomeImplementation();
IManager<IBusinessObject> m2 = m1;
m2.Add(new SomeArbitraryBusinessObject());
An account manager is only meant to manage accounts, not just any business object.
One option is to use two generic type parameters instead of one for ManagerFactory.Create:
public TManager Create<TManager,TObjectType>()
where TManager : IManager<TObjectType>
where TObjectType : IBusinessObject

Categories