Constraining T to a common generic property - c#

I have the following:
public interface IEntity
{
IEntityContent Data { get; set; }
}
public interface IEntityContent { }
public interface IThingService<T>
{
void DoThing(T item);
}
public class BaseEntity<T> : IEntity
where T : IEntityContent
{
public abstract T Data { get; set; }
}
public class FooEntity : BaseEntity<FooContent>
{
public override FooContent Data { get; set; }
}
public class FooContent : IEntityContent
{
// Some properties
}
public class ThingService<T> : IThingService<T>
where T : IEntity
{
public void DoThing(T item)
{
Serializer.Instance.Serialize(item.Content);
}
}
The signature of Serializer.Instance.Serialize is:
string Serialize<T>(T from)
But I get the following:
'BaseEntity<T>' does not implement interface member 'IEntity.Data'. 'BaseEntity<T>.Data' cannot implement 'IEntity.Data' because it does not have the matching return type of 'IEntityContent'
Why is this? As it stands, I am forced to create a bunch of near-identical strongly-typed implementations of IThingService - which is a shedload of duplication - just to specify different type arguments which, as far as I can see, should be generic.
Is this somehow related to a lack of covariance in BaseEntity? How can I make ThingService<T> work?

Why not simply have:
public interface IEntity<T>
{
T Data { get; set; }
}
For an implementation of the interface has to match the interface (as you've not declared any contra/covariance), including the return types (hence your error message)

Related

C# How to create a collection of generic class which derives from a specific implementation of another generic class which is a derived class

Struggling with wording that title to make any sense so I will just show my code and try and explain it
So I have this base class
public abstract class SpacecraftProperty
{
}
public abstract class SpacecraftProperty<T> : SpacecraftProperty
where T : SpacecraftProperty<T>
{
public abstract void Add(T property);
}
With this I can create a collection of SpacecraftProperty just fine thanks to the base SpacecraftProperty class that is not generic.
Then I have this class that derives from SpacecraftProperty that is also generic
public class ResourceStorage<ResourceType> : SpacecraftProperty<ResourceStorage<ResourceType>>
where ResourceType : ResourceInfo, new()
{
public ResourceType resource { get; private set; } = new ResourceType();
public float capacity { get; private set; }
public override void Add(ResourceStorage<ResourceType> property)
{
this.capacity += property.capacity;
}
}
Now the issus is that I need to create a collection of this ResourceStorage class, where each object in the collection can be of any type for ResourceType. But I can't use the same trick as above where I create a non-generic base class as then when I try and derive from SpacecraftProperty<>, it won't know what type of the generic SpacecraftProperty to derive from.
For completeness, here is the code for ResourceInfo, which is an abstract class which will be derived from
public abstract class ResourceInfo
{
public abstract string GetName();
public abstract string GetDescription();
}
So in summary I would for example want a collection that contains say a ResourceStorage<SomeTypeA>, ResourceStorage<SomeTypeB> etc. where SomeTypeA and SomeTypeB derive from ResourceInfo. And ResourceStorage derives from SpacecraftProperty, which has the generic method Add.
Any ideas?
But I can't use the same trick as above where I create a non-generic base class as then when I try and derive from SpacecraftProperty<>, it won't know what type of the generic SpacecraftProperty to derive from.
You can use the same trick. The type to derive from is SpacecraftProperty<ResourceStorage>, where ResourceStorage is the non-generic type you have created. This is because the implementation of Add in the generic ResourceStorage<T> doesn't actually use T.
// note that I've changed the naming conventions to match that of C#'s
public abstract class ResourceStorage: SpacecraftProperty<ResourceStorage> {
public abstract float Capacity { get; protected set; }
}
public class ResourceStorage<TResource> : ResourceStorage
where TResource: ResourceInfo, new()
{
public TResource Resource { get; private set; } = new TResource();
public override float Capacity { get; protected set; }
public override void Add(ResourceStorage property)
{
Capacity += property.Capacity;
}
}
ResourceStorage<TResource> is now a SpacecraftProperty<ResourceStorage>, however, and not a SpacecraftProperty<ResourceStorage<TResource>>.
To fix that, you can make the abstract classes contravariant interfaces instead. Since there is only one Add method:
public interface ISpacecraftProperty {}
public interface ISpacecraftProperty<in T> : ISpacecraftProperty
where T : ISpacecraftProperty<T>
{
void Add(T property);
}
public interface IResourceStorage: ISpacecraftProperty<IResourceStorage> {
public float Capacity { get; }
}
public class ResourceStorage<TResource> : IResourceStorage
where TResource: ResourceInfo, new()
{
public TResource Resource { get; private set; } = new TResource();
public float Capacity { get; private set; }
public void Add(IResourceStorage property)
{
Capacity += property.Capacity;
}
}
Now this compiles:
var list = new List<IResourceStorage>() {
new ResourceStorage<Foo>(),
new ResourceStorage<Bar>()
};
// This'd work too, but I'm sure you are aware already, since it is the nature of what you are trying to do
list[0].Add(list[1]);
ISpacecraftProperty<ResourceStorage<Foo>> x = new ResourceStorage<Foo>();
If ISpacecraftProperty has other methods that return a T, and so can't be contravariant, you can always just add ISpacecraftProperty<ResourceStorage<TResource>> as yet another interface of ResourceStorage<TResource>.
public class ResourceStorage<TResource> : IResourceStorage, ISpacecraftProperty<ResourceStorage<TResource>>
where TResource: ResourceInfo, new()
{
public TResource Resource { get; private set; } = new TResource();
public float Capacity { get; private set; }
public void Add(IResourceStorage property)
{
Capacity += property.Capacity;
}
// implement explicitly by delegation
void ISpacecraftProperty<ResourceStorage<TResource>>.Add(ResourceStorage<TResource> property) {
Add(property);
}
}

Cannot implement interface member because it doesn't have matching return type

I'm working on an abstract algebra library for C#, but am having trouble with implementing interfaces. I have gotten the implementation to work for certain groups, but attempting to create rings is giving me some serious problems. In particular, I've got the following:
public class Scaffolding {
public interface IMonoid<T> : ISemiGroup<T> {
T Identity { get; set; }
}
public interface IGroup<T> : IMonoid<T> {
T Inverse(T a);
}
public interface IRing<T> {
IGroup<T> AdditiveStructure { get; set; }
IMonoid<T> MultiplicativeStructure { get; set; }
}
}
public class ModularMonoid : Scaffolding.IMonoid<int> {
// Implements all necessary monoid properties
}
public class ModularGroup : Scaffolding.IGroup<int> {
// Implements all necessary group properties
}
public class ModularRing : Scaffolding.IRing<int> {
public ModularGroup AdditiveStructure { get; set; }
public ModularMonoid MultiplicativeStructure { get; set; }
// Implement ring-specific properties
}
I get an error stating that 'ModularRing' does not implement interface member 'Scaffolding.IRing.AdditiveStructure'. 'ModularRing.AdditiveStructure' cannot implement 'Scaffolding.IRing.AdditiveStructure' because it does not have the matching return type of 'Scaffolding.IGroup'. I get a similar error for the MultiplicativeStructure. This is strange to me, because both the ModularGroup and ModularMonoid implement IGroup and IMonoid respectively.
Yes, those classes implement the interfaces, but that interface doesn't say "The type of the AdditiveStructure property is something that implements IGroup<T>" - it says that the type of the AdditiveStructure property is IGroup<T>. To implement the interface, you have to match return types exactly.
If you want to be able to implement the interface like that, you'd need to change the interface, potentially like this:
public interface IRing<T, TGroup, TMonoid>
where TGroup : IGroup<T>
where TMonoid : IMonoid<T>
{
TGroup AdditiveStructure { get; set; }
TMonoid MultiplicativeStructure { get; set; }
}
Then implement it as:
public class ModularRing : Scaffolding.IRing<int, ModularGroup, ModularMonoid>
{
public ModularGroup AdditiveStructure { get; set; }
public ModularMonoid MultiplicativeStructure { get; set; }
}
Alternatively, you should consider making the properties read-only. That way, if you're happy with the ModularRing users only using the IGroup<int> and IMonoid<int> definitions (rather than depending on anything extra exposed on ModularGroup and ModularMonoid) then you could stick with just the single type parameter, which would simplify things quite a lot. For example:
public interface IMonoid<T> : ISemiGroup<T>
{
T Identity { get; }
}
public interface IGroup<T> : IMonoid<T>
{
T Inverse(T a);
}
public interface IRing<T>
{
IGroup<T> AdditiveStructure { get; }
IMonoid<T> MultiplicativeStructure { get; }
}
Implementation:
public class ModularRing : Scaffolding.IRing<int>
{
public IGroup<int> AdditiveStructure { get; } = new ModularGroup();
public IMonoid<int> MultiplicativeStructure { get; } = new ModularMonoid();
}
(Or accept them in the constructor; I don't know enough about what you're trying to do with them.)

I would like to use Interface inheritance in a simple way that is hard to not understand

I have a Entity Framework Db Context that is injected via dependency injection into three parts of a system that all have some different needs via three separate interfaces.
We need to flow information to the Save method in different ways and today we have a single interfaces for each type like this.
public interface IIntegrationDbContext
{
IDbSet<DataOne> DataOnes { get; set; }
IDbSet<DataTwo> DataTwos { get; set; }
Task<int> SaveChangesAsync(Customer customer);
}
public interface IProfessionalDbContext
{
IDbSet<DataOne> DataOnes { get; set; }
IDbSet<DataTwo> DataTwos { get; set; }
Task<int> SaveChangesAsync(ProfessionalUser user);
}
public interface IPublicDbContext
{
IDbSet<DataOne> DataOnes { get; set; }
IDbSet<DataTwo> DataTwos { get; set; }
Task<int> SaveChangesAsync(PublicDomain domain);
}
This kind of work but it is a bit of a pain when you start to add a bunch of IDbSets and developers that has to remember to add them to all interfaces, like me :)
So I would like to move all IDbSets to a common base Interface as we have done in the implementation where we have an abstract base class that declares all of these IDbSets and configurations for them.
public interface ICommonBaseDbContext
{
IDbSet<DataOne> DataOnes { get; set; }
IDbSet<DataTwo> DataTwos { get; set; }
}
public interface IPublicDbContext : ICommonBaseDbContext
{
Task<int> SaveChangesAsync(PublicDomain domain);
}
But one big drawback with this is that all have to know why we have a common base interface called ICommonBaseDbContext and never try to use that so in a perfect world I would like to have an construct like
public abstract interface ICommonBaseDbContext
to give a hint on what is going on like in a abstract class.
But I have not found any better way to solve this than the solution with three separate interfaces.
Have I missed any simple and good way to solve this?
Is it not just:
edit: as the only real variance in the example you show is the parameter type of SaveChangesAsync, my first instinct would be to simply make that parameter generic:
public interface IIntegrationDbContext<TUser>
{
IDbSet<DataOne> DataOnes { get; set; }
IDbSet<DataTwo> DataTwos { get; set; }
Task<int> SaveChangesAsync(TUser user);
}
I'm assuming the following:
You want to share the DbSet properties but you want to override the Save part of the process
You want the explicit DbContext-like interfaces for unit testing and/or dependency injection
This is the first iteration of the design I would think (sorry for the amount of code):
// Common Interfaces
public interface ICommonDbSets
{
IDbSet<DataOne> DataOnes { get; }
IDbSet<DataTwo> DataTwos { get; }
}
public interface IUnitOfWork<TUser>
{
Task<int> SaveChangesAsync(TUser user);
}
// Specific interface aggregation
public interface IIntegrationDbContext : ICommonDbSets, IUnitOfWork<Customer>
{
}
public interface IProfessionalDbContext : ICommonDbSets, IUnitOfWork<ProfessionalUser>
{
}
public interface IPublicDbContext : ICommonDbSets, IUnitOfWork<PublicDomain>
{
}
// Base class to enforce contract
public abstract class AbstractDbContext<TUser> : ICommonDbSets, IUnitOfWork<TUser>
{
private readonly DbContext _dbContext;
protected AbstractDbContext(DbContext dbContext)
{
_dbContext = dbContext;
}
public IDbSet<DataOne> DataOnes
{
get { return _dbContext.Set<DataOne>(); }
}
public IDbSet<DataTwo> DataTwos
{
get { return _dbContext.Set<DataTwo>(); }
}
public abstract Task<int> SaveChangesAsync(TUser user);
}
// Specific implementations
public sealed class IntegrationDbContext : AbstractDbContext<Customer>, IIntegrationDbContext
{
public IntegrationDbContext(DbContext dbContext) : base(dbContext)
{
}
public override Task<int> SaveChangesAsync(Customer user)
{
throw new NotImplementedException();
}
}
public sealed class ProfessionalDbContext : AbstractDbContext<ProfessionalUser>, IProfessionalDbContext
{
public ProfessionalDbContext(DbContext dbContext) : base(dbContext)
{
}
public override Task<int> SaveChangesAsync(ProfessionalUser user)
{
throw new NotImplementedException();
}
}
public sealed class PublicDbContext : AbstractDbContext<PublicDomain>, IPublicDbContext
{
public PublicDbContext(DbContext dbContext) : base(dbContext)
{
}
public override Task<int> SaveChangesAsync(PublicDomain user)
{
throw new NotImplementedException();
}
}
EDIT: Notice that this will force you to implement whatever you add to ICommonDbSets in the abstract base class, and it will also force you to override the SaveChangesAsync in the concrete DbContext implementation
EDIT2: Notice that you do not need to implement ICommonDbSets in your concrete classes (IntegrationDbContext, ProfessionalDbContext, PublicDbContext) because they are already implemented in your abstract base class

Getting properties from generic type used in abstract superclass without using interfaces?

Is there any nice, elegant way to get properties from generic type used in abstract superclass without using interfaces?
Here's an example:
public abstract class CoolBase<T>
where T : class
{
private IEnumerable<T> somEnumerable;
public void GetPersonProperties()
{
var name = somEnumerable.First().Name; //this doesn't work
}
}
public class CoolA : CoolBase<Person>
{
}
public class Person
{
public string Name { get; set; }
public string Region { get; set; }
}
}
The goal of using generic classes is type-flexibility -
therefore it makes no sence to declare a method in a generic class which
uses Person-specific methods.
You should implement such detailed methods in the concrete implementations of
your abstract, generic class (here CoolA).
Maybe it is helpful for you to declare an abstract method getProperties()
int the generic, abstract class, wich can be implemented in CoolA via using
Person-specific code.
public abstract class CoolBase<T>
where T : class
{
private IEnumerable<T> somEnumerable;
public abstract void getProperties();
}
public class CoolA : CoolBase<Person>
{
public override void getProperties()
{
//should work, somEnumberable is made of Persons here
var name = somEnumerable.First().Name;
}
}
It makes no sense to put GetPersonProperties in CoolBase. CoolBase is generic, so should not have a class-specific functionality within it.
You could create a abstract method in CoolBase and implement it in your derived type:
public abstract class CoolBase<T> where T : class
{
protected IEnumerable<T> somEnumerable;
public abstract void GetProperties();
}
public class CoolA : CoolBase<Person>
{
public override void GetProperties()
{
var name = somEnumerable.First().Name;
}
}
public class Person
{
public string Name { get; set; }
public string Region { get; set; }
}
Alternatively, you could you reflection to get at the properties of T at runtime:
public abstract class CoolBase<T> where T : class
{
private IEnumerable<T> somEnumerable;
public void GetProperties()
{
foreach (var prop in typeof (T).GetProperties())
{
// do something with each property
}
}
}

Derived Interface with derived interface member

I have 2 base interfaces, IViewBase (which all views will implement) and IPresenterBase (which all presenters will implement):
public interface IViewBase { }
public interface IPresenterBase
{
IViewBase View { get; set; }
}
Then i've created a new interface ILogPresenter that derives from IPresenterBase and ILogView deriving from IViewBase:
public interface ILogPresenter : IPresenterBase { }
public interface ILogView : IViewBase{ }
When i create a class that implements ILogPresenter,
public class LogPresenter: ILogPresenter
{
public ILogView View { get; set; }
}
I get an error:
'LogPresenter' does not implement interface member 'IPresenterBase.View'. 'LogPresenter.View' cannot implement 'IPresenterBase.View' because it does not have the matching return type of 'Views.IViewBase'.
I cannot set the return type of LogPresenter.View to ILogView which derives from IViewBase? I would like implement ILogPresenter with a different IView which derives from IViewBase.
You probably want to use generics here:
public interface IViewBase { }
public interface IPresenterBase<T> where T : IViewBase
{
T View { get; set; }
}
then:
public interface ILogPresenter : IPresenterBase<ILogView> { }
public interface ILogView : IViewBase{ }
Aside from covariant return types not being supported in C# (which would cause a problem even just with a getter) you have a fundamental problem in the original. I could do:
IPresenterBase foo = new LogPresenterImpl();
foo.View = new SomeOtherView();
where SomeOtherView implemented IVewBase but not ILogView. How would you expect your property to cope with that?
The generic version above solves this by allowing a presenter to express what kind of view it's presenting.
You can do this with generics:
public interface IViewBase { }
public interface IPresenterBase<T> where T : IViewBase
{
T View { get; set; }
}
public interface ILogPresenter : IPresenterBase<ILogView> { }
public interface ILogView : IViewBase { }
public class LogPresenter : ILogPresenter
{
public ILogView View { get; set; }
}

Categories

Resources