Classes coupled with each other by generics - c#

I'm currently learning C# and I'm coming from Java. In Java, I can do something like this:
public interface BasePresenterView<T extends BaseActivityPresenter> {}
public interface BaseActivityPresenter<T extends BasePresenterView> {}
and in C# I'm having a hard time achieving the same thing.

This is called generic constrained in .NET.
Your example in C# will look like this.
public interface BasePresenterView<T> where T: 'Interface or Type'{}
public interface BaseActivityPresenter<T> where T: 'Interface or Type' {}
More info: https://msdn.microsoft.com/en-us/library/d5x73970.aspx
EDIT: As already mentioned circular dependencies are not possible in C#, so you can not constraint them in the same way as in your example.

You want something like this if you are using abstract base classes...
public abstract class MyBasePresenter
{
}
public abstract class MyBasePresenter<TView> : MyBasePresenter
where TView : MyBaseView
{
}
public abstract class MyBaseView
{
}
public abstract class MyBaseView<TPresenter> : MyBaseView
where TPresenter : MyBasePresenter
{
}
public class MyView : MyBaseView<MyPresenter>
{
}
public class MyPresenter : MyBasePresenter<MyView>
{
}
... do this if you want interfaces ...
public interface IMyPresenter
{
}
public interface IMyPresenter<TView> : IMyPresenter
where TView : IMyView
{
}
public interface IMyView
{
}
public interface IMyView<TPresenter> : IMyView
where TPresenter : IMyPresenter
{
}
public class MyView : IMyView<MyPresenter>
{
}
public class MyPresenter : IMyPresenter<MyView>
{
}
... of you really want to go crazy you can even nest the generics ...
public class MyView : IMyView<IMyPresenter<IMyView>>
{
}

You can use generic type constraints, which restrict compile-time types using inheritance.
public interface BasePresenterView<T> where T : IBaseView
Because inheritance cannot be circular, you cannot have the circular dependancy as in the question.
However, if they declare the same methods or properties, they could inherit the same interface:
public interface BasePresenterView : IBaseView
public interface BaseActivityPresenter : IBaseView

I think I resolved it :)
public interface BaseActivityPresenter<T, K>
where T : BasePresenterView<T, K>
where K : BaseActivityPresenter<T, K> {}
public interface BasePresenterView<T, K>
where T : BasePresenterView<T, K>
where K : BaseActivityPresenter<T, K> {}
That seems to be working... for now. I don't know if this is a proper approach.

Related

How to reuse generic interface?

I'm using ASP.NET Boilerplate, on ASP.NET Core.
I have some model classes.
public class AClass : FullAuditedEntity<int>
{
}
and one interface like below.
public interface ISomeInterface<T, TEntity> where T : BaseFileEntity where TEntity : class, IEntity<int>
{
}
Now If I add one extra model class like below.
public class BClass : FullAuditedEntity<string>
{
}
I have to define another interface for this change like below.
public interface ISomeInterface<T, TEntity> where T : BaseFileEntity where TEntity : class, IEntity<string>
{
}
So basically it's duplicate code. Is there any better way to do this?
Is it possible to declare your interface like the one below so you can pass the type to IEntity
public interface ISomeInterfaceB<T, TEntity, TK> where T : BaseFileEntity where TEntity : class, IEntity<TK> {
}
You can Change the interface to:
public interface ISomeInterface<T, TEntity, U> where T : BaseFileEntity where TEntity : class, IEntity<U>
{
}

Passing type parameter to interface of base class

StateBase implements iState
WolfState ineherits from StateBase
WolfController inherits from ControllerBase
I want to be able to do a
WolfState that uses WolfController inheriting from State
SheepState
that uses SheepController and inherits from State
WolfController and SheepController would both inherit from StateController.
How should i declare WolfState?
The way i try to do it doesnt work.
public interface IState <T> where T : StateController
{
}
public abstract class State<T> where T : StateController, IState<T>
{
}
// THIS IS HOW I WOULD LIKE TO DO IT BUT ITS NOT ACCEPTED
public class WolfState : State<WolfController>
{
}
public class SheepState : State<SheepController>
{
}
It looks like you intend State<T> to implement IState<T>. You currently have a constraint on the type of T. Change the definition of State<T> to:
public abstract class State<T> : IState<T> where T : StateController
{
}

Can someone explain why this isn't valid implementation?

public class ServiceCodeController : ControllerBase {
// the red squiggly under IJobRepository is saying it's not convertible
private LazyRepo<IJobRepository> _domainRepo2;
}
public class LazyRepo<TRepo> where TRepo : IRepository<IDomainEntity> { ... }
public interface IJobRepository : IRepository<JobDomain>, IListRepository { ... }
public interface IRepository<T> : IRepositoryRead<T>,
IRepositoryCreate<T>,
IRepositoryDelete<T>,
IRepositoryUpdate<T>
where T : IDomainEntity { ... }
public class JobDomain : BaseDomainEntity { ... }
public abstract class BaseDomainEntity : IDomainEntity,
IDomainEntityModifiable,
IDomainEntityActivatable,
IDomainEntityNameable { ... }
My thinking is that LazyRepo takes something that implements IRepository that takes something that implements IDomainEntity. As you can see, IJobRepository implements IRepository that takes JobDomain that inherits from BaseDomainEntity which, at long last, implements IDomainEntity.
For my money, this should work for setting up the LazyRepo class.
Can someone explain to me why I'm getting this error? The type 'IJobRepository' cannot be used as type parameter 'TRepo' in the generic type or method 'LazyRepo'. There is no implicit reference conversion from 'IJobRepository' to 'IRepository'
I think this is where the concepts of contravariance and covariance come in.
A covariant interface allows its methods to return more derived types than those specified in the interface. A contravariant interface allows its methods to accept parameters of less derived types than those specified in the interface.
source: https://msdn.microsoft.com/en-us/library/dd465120.aspx
You fix this by using the in and out keywords:
public interface IRepository<out T> : ...
(source: https://msdn.microsoft.com/en-us/library/dd997386.aspx)
Try this:
public class ServiceCodeController : ControllerBase {
// the red squiggly under IJobRepository is saying it's not convertible
private LazyRepo<IJobRepository, JobDomain> _domainRepo2;
}
public class LazyRepo<TRepo, TDomain> where TRepo : IRepository<TDomain> where TDomain : IDomainEntity { }
By specifying the TDomain as a generic parameter constrained to IDomainEntity and constraining TRepo to IRepository of TDomain, you provide all of the information needed by the compiler to resolve IJobRepository and JobDomain as arguments for LazyRepo. This provides an alternative to using variance.
The issue has to deal with the fact that IRepository<IDomainEntity> != IRepository<JobDomain>. It's the classic fruit bowl issue that's been discussed on SO. However, if you substitute a generic parameter for IDomainEntity, then you can fully qualify the run-time definition of TRepo for LazyRepo.
For completeness, here is a modified version of your code that compiles:
public class ControllerBase {}
public interface IDomainEntity {}
public interface IDomainEntityModifiable {}
public interface IDomainEntityActivatable {}
public interface IDomainEntityNameable {}
public interface IListRepository {}
public interface IRepositoryRead<out TDomain> where TDomain : IDomainEntity {}
public interface IRepositoryCreate<out TDomain> where TDomain : IDomainEntity {}
public interface IRepositoryDelete<out TDomain> where TDomain : IDomainEntity {}
public interface IRepositoryUpdate<out TDomain> where TDomain : IDomainEntity {}
public class ServiceCodeController : ControllerBase
{
private LazyRepo<IJobRepository, JobDomain> _domainRepo2;
}
public class LazyRepo<TRepo, TDomain> where TRepo : IRepository<TDomain> where TDomain : IDomainEntity { }
public interface IJobRepository : IRepository<JobDomain>, IListRepository { }
public interface IRepository<out T> : IRepositoryRead<T>,
IRepositoryCreate<T>,
IRepositoryDelete<T>,
IRepositoryUpdate<T>
where T : IDomainEntity { }
public class JobDomain : BaseDomainEntity { }
public abstract class BaseDomainEntity : IDomainEntity,
IDomainEntityModifiable,
IDomainEntityActivatable,
IDomainEntityNameable { }

Conversion of generic types

I have below class structure:
public class FirstContextController : AFirstContextController
public abstract class AFirstContextController : ABaseController<AFirstContextView>
public abstract class ABaseController<TView> where TView : ABaseView
public abstract class AFirstContextView : ABaseContextView
public abstract class ABaseContextView : ABaseView
public abstract class ABaseView : UserControl
Now I create an instance of my controller:
AFirstContextController firstContextController = new FirstContextController();
and need to return it from the method:
public static ABaseController<ABaseContextView> GetContextController( ... )
{
return firstContextController;
}
But then I get an error:
Cannot implicitly convert type 'AFirstContextController' to 'ABaseController'
So I tryied many casting (for example):
return (ABaseController<AFirstContextView>)firstContextController;
return (ABaseController<ABaseContextView>)( (ABaseController<AFirstContextView>)firstContextController );
But still get similar errors according to conversion.
So what conversion should I apply to return it ?
You need an interface derived from ABaseController<ABaseContextView> to make it work.
This has to do with variance and covariance. Reed more about it on MSDN: Covariance and Contravariance in Generics.
You will have to mark the ABaseContextView with out in your interface definition, so the signature should be something like IBaseController<out ABaseContextView>.
Example:
public abstract class ABaseController<TView> : IBaseController<TView> where TView : ABaseView
{
public void SomeMethod() { }
}
And the interface:
public interface IBaseController<out TView> where TView : ABaseView
{
void SomeMethod();
}
Then your current method should read:
public static IBaseController<ABaseContextView> GetContextController( ... )
{
return firstContextController;
}

How do I use a generic base class two levels down in an inheritance tree?

Imagine that I have a generic base class like this:
public abstract class AnimalDTO<TA, TB>
{
public static TB ToDTO(TA entity)
{
return ConvertToDTO<TB>(entity);
}
}
The class is responsible for being able to convert a passed-in entity to a DTO.
I have a class that uses this generic class:
public class MammalDTO<Mammal, MammalDTO>
{
// omitted stuff here
}
A user can now use MammalDTO.ToDTO(mammal) to convert a Mammal to a MammalDTO.
Now I want to derive off Mammal:
public class Koala<???, ???> : Mammal<???, ???>
How do I do this?
One solution would be to makeMammalDTOan open but constrained generic type.
You could then try to constrain the generic further and further as you go down the inheritance hierarchy, closing it at a leaf. It may also be suitable to use abstract classes as appropriate, but this is not a requirement of the pattern.
To be honest though, I don't really like the 'self-referencing generic' pattern; I find it quite confusing.
public abstract class Animal { }
public abstract class Mammal : Animal { }
public sealed class Koala : Mammal { }
public abstract class AnimalDTO<TAnimal, TDTO> where TAnimal : Animal
{
public abstract TDTO ConvertToDTO(TAnimal entity);
}
public abstract class MammalDTO<TMammal, TMammalDTO> : AnimalDTO<TMammal, TMammalDTO>
where TMammal : Mammal
where TMammalDTO : MammalDTO<TMammal, TMammalDTO>{}
public sealed class KoalaDTO : MammalDTO<Koala, KoalaDTO>
{
public override KoalaDTO ConvertToDTO(Koala entity)
{
throw new NotImplementedException();
}
}

Categories

Resources