the chosen title for my question isn't quite precise, but I don't know the term I'm looking for or if it is even possible.
What I have in mind is a chain of consumer <- procucer <-product.
A consumer can "consume" producers and producers "produce" products of a certain type. Therefor I wrote:
public interface IProduct
{
string ProductName { get; }
}
public class Product : IProduct
{
public string ProductName { get { return "name of product"; } }
}
public interface IProducer<T>
{
T ProducerProperty { get; set; }
void ProducerMethod();
}
public class Producer<T> : IProducer<T> where T : IProduct
{
public Producer()
{
}
public T ProducerProperty { get; set; }
public void ProducerMethod()
{
}
}
public interface IConsumer<T>
{
T ConsumerProperty { get; set; }
void ConsumerMethod();
}
public class Consumer<T> : IConsumer<T>
{
private U producer; //U should be IProducer<IProduct>, doesen't work
public Consumer(U producer) //U should be IProducer<IProduct>, doesen't work
{
this.producer = producer;
}
public T ConsumerProperty { get; set; }
public void ConsumerMethod()
{
}
}
and the use case:
private IProducer<IProduct> producer; //DeviceManager
private IConsumer<IProducer<IProduct>> consumer; //DeviceViewManager
public MainPage()
{
this.InitializeComponent();
producer = new Producer<IProduct>();
consumer = new Consumer<IProducer<IProduct>>();
}
The consumer class uses the generic "U", which is imaginary at this point. I want the consumer class to use the type U. In the context of the given example you could think of a user who consumes different types of noodles from different manufacrures of noodles.
I want the generic classes be tied to the interfaces rather than to actual classes. But I culdn't manage to achieve this. I tried substituting the interfaces with base classes (e.g.: ProducerBase), but than the actual base classes were needed.
The Problem
If you define the Consumer class like this
public class Consumer<T, U> : IConsumer<T, U>
{
...
}
you get the errors
CS0314 The type 'T' cannot be used as type parameter 'T' in the generic type or method IConsumer<T, U>'. There is not boxing conversion or type parameter conversion from 'T' to ''IProducer'.
and
CS0314 The type 'U' cannot be used as type parameter 'U' in the generic type or method IConsumer<T, U>'. There is not boxing conversion or type parameter conversion from 'U' to ''IProducer'.
What this tells you is that you cannot derive from the IConsumer<T, U> interface, because it has constraints for its generic type parameters:
public interface IConsumer<T, U>
where T : IProducer<U> // T needs to "be" an IProducer<U>
where U : IProduct // U needs to "be" an IProduct
{
...
}
because the interface expects T and U to derive from IProducer<U> and IProduct respectively and the compiler doesn't know that the generic type parameters T and U of the Consumer class can actually be converted to an IProducer<U> and IProduct respectively.
The Solution
Therefore, if you define the Consumer like this and add constraints for T and U
public class Consumer<T, U> : IConsumer<T, U>
where T : IProducer<U>
where U : IProduct
{
...
}
it will work because now you specified that T and U will always be an IProducer<U> and IProduct respectively.
I hope above explanation was understandable; either way, you should also read this for a good understanding of constraints on generic types.
Related
If a class satisfies a covariant interface with a derived class as type parameter, why is that not enough to satisfy a constraint for the same interface with the base class as type parameter?
Given the following code
public interface ICovariantInterface<out T>
{
T Value { get; }
}
public class Base { public int Value { get; set; } }
public class Sub : Base { public int OtherValue { get; set; } }
public class A : ICovariantInterface<Sub>
{
Sub _sub = new Sub { Value = 1, OtherValue = 2 };
public Sub Value { get { return _sub; } }
}
public class B : A
{
ICovariantInterface<Base> MeAsInterface { get { return this; } }
}
public interface OtherInterface : ICovariantInterface<Base>
{ int ThirdValue { get; } }
public class C : A, OtherInterface
{
ICovariantInterface<Base> MeAsInterface { get { return this; } }
public int ThirdValue { get { return 2; } }
}
Class B works fine - since A satisfies the covariant interface ICovariantInterface with type parameter Sub, it can immediately be converted to the same interface with type parameter Base - but C fails to compile, with error
'CovarianceTest.C' does not implement interface member 'CovarianceTest.ICovariantInterface.Value'. 'CovarianceTest.A.Value' cannot implement 'CovarianceTest.ICovariantInterface.Value' because it does not have the matching return type of 'CovarianceTest.Base'.
How can C fail to satisfy the constraint when it can immediately be converted to the interface specified in the constraint?
This problem has made it quite difficult for me to take full advantage of the possibilites offered by covariance.
Covariance (and contravariance) establish rules for consuming types with type parameters marked with out (or in), in that, if you have an instance of a particular type certain implicit conversions are available to you, as the consumer.
In your example, B is consuming an instance of A (that happens to be itself, but need not have been) at the point at which your allowed cast occurs.
But in your C example, you're attempting to implement an interface. Covariance and Contravariance don't apply here - if the interface states that a method returns a T then the implementation has to provide a method that returns exactly the type substituted for T.
I am stuck in implementation of generic class and interface. I am not sure what i wanted to do is possible or not.
here is my code:-
I have defined a generic class whose type not define like
public class Response<T>
{
public T Data { get; set; }
public Response()
{
Data = default(T);
}
}
and a Interface which right now have one function
public interface IOInterface<T> where T : Response<T>
{
Response<T> ReadAdvantechChannel(Dictionary<string, string> IOParameters);
}
And one derive class who will implements this interface :-
public class AdvantechOperation : IOInterface<T>
{
public Response<AIRecordInfo> ReadAdvantechChannel(Dictionary<string, string> IOParameters)
{
Response<AIRecordInfo> resp = new Response<AIRecordInfo>();
}
}
and AIrecordinfo is again class
public class AIRecordInfo
{
double[] ArryMaxValueAIdouble;
double[] ArryMinValueAIdouble;
double[] ArryAVGValueAIdouble;
}
what i want to do that implement a class who implement define interface but return type of interface function is not clear for my future derived class thats why i have made it generic and try to implement it my present class "AdvantechOperation". My interface will have three functions and all function return type will different to each other thats why i made it generic return type Response and in my present class AdvantechOperation, T replaced by AIRecordInfo.
But I am facing below Error while compiling this project
Error 7 The type 'T' cannot be used as type parameter 'T' in the
generic type or method 'IOModule.IOInterface'. There is no boxing
conversion or type parameter conversion from 'T' to
'IOModule.Response'. D:\MTS\SMS\DAQAdvantechdll\DAQAdvantechdll\IOCommunication.cs 28 18 IOModule
Error 8 'IOModule.AdvantechOperation' does not implement interface
member
'IOModule.IOInterface.ReadAdvantechChannel(System.Collections.Generic.Dictionary)'.
'IOModule.AdvantechOperation.ReadAdvantechChannel(System.Collections.Generic.Dictionary)'
cannot implement
'IOModule.IOInterface.ReadAdvantechChannel(System.Collections.Generic.Dictionary)'
because it does not have the matching return type of
'IOModule.Response'. D:\MTS\SMS\DAQAdvantechdll\DAQAdvantechdll\IOCommunication.cs 28 18 IOModule
Get rid of the generic type constraint for your interface and use AIRecordInfo as the generic type argument when implementing the interface:
public class AdvantechOperation : IOInterface<AIRecordInfo>
{ ... }
It seems to me that you want this:
public class Response<T> { }
public interface IOInterface<T> where T : Response<T>
{
T ReadAdvantechChannel(Dictionary<string, string> IOParameters);
}
public class AdvantechOperation : IOInterface<AIRecordInfo>
{
public AIRecordInfo ReadAdvantechChannel(Dictionary<string, string> IOParameters)
{
return new AIRecordInfo();
}
}
public class AIRecordInfo : Response<AIRecordInfo>
{
double[] ArryMaxValueAIdouble;
double[] ArryMinValueAIdouble;
double[] ArryAVGValueAIdouble;
}
Response<T> no longer needs a body because IOInterface<T>'s method simply returns T which must inherit from Response<T>.
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 { }
I need to make some of my classes inherit from Interface with generic field
like that
public Interface ICommon<Ttype>
{
Ttype Filed{get;set;}
}
public Class class1:Icommon<int>
{
int Filed{get;set;}
}
public Class class2:Icommon<double>
{
double Filed{get;set;}
}
I created a generic class with constraints that uses classes class1 and class2 to make some operations like that:
public Class GenericClass<Ttype,Tcommon> where Ttype:ICommon<Tcommon>
{
//forexample
public Ttype someOperation(Ttype x)
{
var a=x.Field;
//.............
}
}
every time I use the GenericClass I have to know the type of Field of the class I used say class1 or class2 to be able to pass it to match the generic constraint
Is there a way to write GenericClass like that:
public Class GenericClass<Ttype,Tcommon> where Ttype:**ICommon**
{
//forexample
public Ttype someOperation(Ttype x)
{
var a=x.Field;
//.............
}
}
by writting ICommon without <TCommon> ??
Update:
or how to edit ICommon interface to be like that
public Interface ICommon
{
Ttype Filed{get;set;}
}
I hope I understood what you intended to do:
public interface ICommon<T>
{
T Field { get; set; }
}
public class GenericClass<T>
{
public ICommon<T> SomeOperation(ICommon<T> x)
{
// do your stuff
}
}
Short answer is: no.
You need to tell the compiler type of generic argument.
In fact, GenericClass<int> and GenericClass<string> are two different classes in CLR.
Lets say I have this arrangement:
public interface ICreatable
{
int CreatedByUserId { get; set; }
}
public class Unicorn : ICreatable
{
public int CreatedByUserId { get; set; }
}
public interface ICrudService<T>
where T : class, ICreatable
{
T DoSomething(T t);
}
public class UnicornService : ICrudService<Unicorn>
{
public Unicorn DoSomething(Unicorn unicorn)
{
var createdByUserId = unicorn.CreatedByUserId;
// ...
return unicorn;
}
}
And use it like so:
static void Main(string[] args)
{
var unicorn = new Unicorn();
var unicornService = new UnicornService();
unicornService.DoSomething(unicorn);
}
This runs fine. However, lets say I want to cast unicornService as it's interface type of ICrudService along with it's generic type to it's interface type as such:
var crudService = unicornService as ICrudService<ICreatable>;
I run into problems. This is how it looks:
unicornService as ICrudService<Unicorn> --> casts is fine
unicornService as ICrudService<ICreatable> --> casts to null
It seems since Unicorn derives from ICreatable and since ICrudService<T> where T: class, ICreatable that it should have no problems working this out. My searches started leading me into Covariance and Contravariances but I'm getting lost at that level.
How can I cast crudService to ICrudService<ICreatable>?
Update:
Using covariance as such:
public interface ICrudService<out T>
Then makes intellisense say "Invalid variance: The type parameter 'T' must be contravariantly valid on 'ICrudService.DoSomething(T)'. 'T' is covariant." How does this work?
An ICrudService<Unicorn> cannot be treated as an ICrudService<ICreatable>.
An ICrudService<Unicorn> object is only allowed to accept parameters of type Unicorn or subtypes of Unicorn, but an ICrudService<ICreatable> can accept a parameter of SomeOtherTypeOfICreatable.
You UnicornService type is allowed to use members specific to Unicorn, not just ICreatable, since that's the type it restricted it's function to. That restriction prohibits it from meeting the more general interface's API.
So, in short, it's not possible.
You should change DoSomething to accept ICreatable instead of T to use out T modifier:
public interface ICrudService<out T>
where T : class, ICreatable
{
T DoSomething(ICreatable t);
}
public class UnicornService : ICrudService<Unicorn>
{
public Unicorn DoSomething(ICreatable t)
{
var unicorn = (Unicorn)t;
var createdByUserId = unicorn.CreatedByUserId; // or t.CreatedByUserId
// ...
return unicorn;
}
}
Please note that this code will throw the exception if non-Unicorn will be passed to UnicornService.