I have to write a class called Vehicle with many attributes (e.g. size, seats, color, ...) and also I have two more classes to write called Trunk and Car with their own attributes.
So I wrote it:
// Vehicle.cs
abstract public class Vehicle
{
public string Key { get; set; }
...
}
// Car.cs
public class Car : Vehicle
{
...
}
// Trunk.cs
public class Trunk : Vehicle
{
...
}
After that, I wrote an Interface:
// IVehicleRepository.cs
public interface IVehicleRepository
{
void Add(Vehicle item);
IEnumerable<Vehicle> GetAll();
Vehicle Find(string key);
Vehicle Remove(string key);
void Update(Vehicle item);
}
So I was thinking that I could use something like this:
// CarRepository.cs
public class CarRepository : IVehicleRepository
{
private static ConcurrentDictionary<string, Car> _cars =
new ConcurrentDictionary<string, Car>();
public CarRepository()
{
Add(new Car { seats = 5 });
}
public IEnumerable<Car> GetAll()
{
return _cars.Values;
}
// ... I implemented the other methods here
}
But, I got errors:
error CS0738: 'CarRepository' does not implement interface member 'IVehicleRepository.GetAll()'. 'CarRepository.GetAll()' cannot implement 'IVehicleRepository.GetAll()' because it does not have the matching return type of 'IEnumerable<'Vehicle>'.
So, how I can do it?
Your CarRepository isn't implementing the method. These two are not the same:
public IEnumerable<Car> GetAll()
IEnumerable<Vehicle> GetAll()
These are two different types and when you derive from the interface you have to implement it exactly. You can just implement it this way:
public IEnumerable<Vehicle> GetAll()
{
// Cast your car collection into a collection of vehicles
}
However the better way would be to make it a generic interface of: (The downside is that the two different implementations type again are two different types so see if that is what you want)
public interface IVehicleRepository<TVehicle> {}
public class CarRepository : IVehicleRepository<Car> {}
A more complete version:
public interface IVehicleRepository<TVehicle> where TVehicle : Vehicle
{
void Add(TVehicle item);
IEnumerable<TVehicle> GetAll();
Vehicle Find(string key);
Vehicle Remove(string key);
void Update(TVehicle item);
}
public class CarRepository : IVehicleRepository<Car>
{
private static ConcurrentDictionary<string, Car> _cars =
new ConcurrentDictionary<string, Car>();
public CarRepository()
{
Add(new Car { seats = 5 });
}
public IEnumerable<Car> GetAll()
{
return _cars.Values;
}
}
You could make the IVehicleRepository generic:
public interface IVehicleRepository<T> where T : Vehicle
{
void Add(T item);
IEnumerable<T> GetAll();
Vehicle Find(string key);
Vehicle Remove(string key);
void Update(T item);
}
and then implement the classes like this:
public class CarRepository : IVehicleRepository<Car>
{
private static ConcurrentDictionary<string, Car> _cars =
new ConcurrentDictionary<string, Car>();
public CarRepository()
{
Add(new Car { seats = 5 });
}
public IEnumerable<Car> GetAll()
{
return _cars.Values;
}
}
But you will then still have the problem that a CarRepository is a IVehicleRepository<Car> while a TruckRepository would be a IVehicleRepository<Truck>. And these two interfaces are different types and only assignable to each other if the have the correct variance.
Related
How it can be fixed? Why I can not to use this constructions?
using System;
public class Program
{
public interface IReadParamModel{ }
public interface IReadResultModel{ }
public interface IWriteParamModel{ }
public interface IWriteResultModel{ }
public interface IDataReader<TParam, TResult> where TParam : IReadParamModel where TResult : IReadResultModel
{
TResult Get(TParam param);
}
public interface IDataWriter<TParam, TResult> where TParam : IWriteParamModel where TResult : IWriteResultModel
{
TResult Write(TParam param);
}
public abstract class BaseReportService<TReader, TWriter>
where TReader : IDataReader<IReadParamModel, IReadResultModel>
where TWriter : IDataWriter<IWriteParamModel, IWriteResultModel>
{
TWriter writer;
TReader reader;
}
public class ReaderParamModel : IReadParamModel { }
public class ReadResultModel : IReadResultModel { }
public class WriteParamModel : IWriteParamModel { }
public class WriteResultModel : IWriteResultModel { }
public class DataReader : IDataReader<ReaderParamModel, ReadResultModel>
{
public ReadResultModel Get(ReaderParamModel param) { return null; }
}
public class DataWriter : IDataWriter<WriteParamModel, IWriteResultModel>
{
public IWriteResultModel Write(WriteParamModel param){ return null; }
}
public class ReportService : BaseReportService<DataReader, DataWriter>
{
}
}
Compilation error (line 46, col 15): The type 'Program.DataReader' cannot be used as type parameter 'TReader' in the generic type or method 'Program.BaseReportService'. There is no implicit reference conversion from 'Program.DataReader' to 'Program.IDataReader'.
Compilation error (line 46, col 15): The type 'Program.DataWriter' cannot be used as type parameter 'TWriter' in the generic type or method 'Program.BaseReportService'. There is no implicit reference conversion from 'Program.DataWriter' to 'Program.IDataWriter'.
The problem is, that IDataReader<IReadParamModel, IReadResultModel> and IDataReader<ReaderParamModel, ReadResultModel> are incompatible types. In order to make them compatible, co-/contravariance would be needed, but with TResult Get(TParam param);, TParam would be contravariant and TResult would be covariant. This means, there is no way to make the two interfaces compatible with their current usage.
The choices are, to use the interfaces directly if access to the implementation properties is not required or to use the concrete types as additional generic parameters. The following code contains three sections that demonstrate the different designs, based on the co-/contravariant IDataReader interface.
The code is restricted to the Reader part, since the examples for reader and writer are quite similar. The Test method is used to highlight some differences in the actually available types at different inheritance levels.
public interface IReadParamModel { }
public interface IReadResultModel { }
public class ReaderParamModel : IReadParamModel { }
public class ReadResultModel : IReadResultModel { }
public interface IDataReader<in TParam, out TResult>
where TParam : IReadParamModel
where TResult : IReadResultModel
{
TResult Get(TParam param);
}
// First variant - much interface usage
public class DataReader_1 : IDataReader<IReadParamModel, ReadResultModel>
{
public ReadResultModel Get(IReadParamModel param) { return null; }
}
public abstract class BaseReportService_1<TReader>
where TReader : IDataReader<IReadParamModel, IReadResultModel>
{
protected TReader reader;
// input is interface, reader.Get result is interface
protected virtual IReadResultModel Test(IReadParamModel param)
{
var result = reader.Get(param);
return result;
}
}
public class ReportService_1 : BaseReportService_1<DataReader_1>
{
// input is interface, reader.Get result is concrete class
protected override IReadResultModel Test(IReadParamModel param)
{
var result = reader.Get(param);
return result;
}
}
// Second variant - less interface usage, more generic parameters
public class DataReader_2 : IDataReader<ReaderParamModel, ReadResultModel>
{
public ReadResultModel Get(ReaderParamModel param) { return null; }
}
public abstract class BaseReportService_2<TReader, TReaderParam>
where TReader : IDataReader<TReaderParam, IReadResultModel>
where TReaderParam : IReadParamModel
{
protected TReader reader;
// input is concrete class, reader.Get result is interface
protected virtual IReadResultModel Test(TReaderParam param)
{
var result = reader.Get(param);
return result;
}
}
public class ReportService_2 : BaseReportService_2<DataReader_2, ReaderParamModel>
{
// input is concrete class, reader.Get result is concrete class
protected override IReadResultModel Test(ReaderParamModel param)
{
var result = reader.Get(param);
return result;
}
}
// Third variant - fully parameterized
public class DataReader_3 : IDataReader<ReaderParamModel, ReadResultModel>
{
public ReadResultModel Get(ReaderParamModel param) { return null; }
}
public abstract class BaseReportService_3<TReader, TReaderParam, TReadResult>
where TReader : IDataReader<TReaderParam, TReadResult>
where TReaderParam : IReadParamModel
where TReadResult : IReadResultModel
{
protected TReader reader;
// input is concrete class, reader.Get result is concrete class
protected virtual TReadResult Test(TReaderParam param)
{
var result = reader.Get(param);
return result;
}
}
public class ReportService_3 : BaseReportService_3<DataReader_3, ReaderParamModel, ReadResultModel>
{
// input is concrete class, reader.Get result is concrete class
protected override ReadResultModel Test(ReaderParamModel param)
{
var result = reader.Get(param);
return result;
}
}
If you need the concrete types for input and output (like in the 3rd example), you should check, if you really need to specify the reader type for the ReportService.
// Fourth variant - decoupled
// the reader is not really needed for this example...
public class DataReader_4 : IDataReader<ReaderParamModel, ReadResultModel>
{
public ReadResultModel Get(ReaderParamModel param) { return null; }
}
public abstract class BaseReportService_4<TReaderParam, TReadResult>
where TReaderParam : IReadParamModel
where TReadResult : IReadResultModel
{
// reader is interface, can be assigned from DataReader_4 or different implementations
protected IDataReader<TReaderParam, TReadResult> reader;
// input is concrete class, reader.Get result is concrete class
protected virtual TReadResult Test(TReaderParam param)
{
var result = reader.Get(param);
return result;
}
}
public class ReportService_4 : BaseReportService_4<ReaderParamModel, ReadResultModel>
{
// input is concrete class, reader.Get result is concrete class
protected override ReadResultModel Test(ReaderParamModel param)
{
var result = reader.Get(param);
return result;
}
}
I am trying to implement the generic repository pattern in my application. I have two interfaces, IEntity and IRepository:
IEntity:
public interface IEntity
{
int Id { get; set; }
}
IRepository:
public interface IRepository<T> where T : IEntity
{
void AddOrUpdate(T ent);
void Delete(T ent);
IQueryable<T> GetAll();
}
And now I'm trying to make a generic RepositoryGlobal class, but I am getting this error:
The type 'T' must be a reference type in order to use it as parameter 'T' in the generic type or method
This is what my code looks like:
public class RepositoryGlobal<T> : IRepository<T> where T : IEntity
{
public RepositoryGlobal(DbContext _ctx)
{
this._context = _ctx;
}
private DbContext _context;
public void Add(T ent)
{
this._context.Set<T>().Add(ent);
}
public void AddOrUpdate(T ent)
{
if (ent.Id == 0)
{
//not important
}else
{
//for now
}
}
public void Delete(T ent)
{
}
public IQueryable<T> GetAll()
{
return null;
}
}
The error appears in the Add method of the RepositoryGlobal class.
Any ideas?
Thanks
You need to add a class constraint
public class RepositoryGlobal<T> : IRepository<T> where T : class, IEntity
I have the class PlayersCollection and I want to interface it in IWorldCollection.
The probleme is about writing the declaration in the interface which cause me this error :
Assets/Scripts/Arcane/api/Collections/ItemsCollection.cs(17,22): error CS0425:
The constraints for type parameter `T' of method
`Arcane.api.ItemsCollection.Get<T>(int)
must match the constraints for type parameter `T' of
interface method `Arcane.api.IWorldCollection.Get<T>(int)'.
Consider using an explicit interface implementation instead
Here is my class and my interface. How to write a generic method implementation with a class constraint ?
public class PlayersCollection : IWorldCollection
{
public Dictionary<Type, object> Collection;
public PlayersCollection()
{
Collection = new Dictionary<Type, object>();
}
public T Get<T>(int id) where T: PlayerModel
{
var t = typeof(T);
if (!Collection.ContainsKey(t)) return null;
var dict = Collection[t] as Dictionary<int, T>;
if (!dict.ContainsKey(id)) return null;
return (T)dict[id];
}
}
}
public interface IWorldCollection
{
T Get<T>(int id) where T : class;// Work when I change "class" to "PlayerModel".
}
Big thanks for your help :)
It seems to me that the following will meet the requirements, by pushing the generic type parameter up to the class/interface level:
public class PlayersCollection<T> : IWorldCollection<T> where T : PlayerModel
{
public Dictionary<Type, T> Collection;
public PlayersCollection()
{
Collection = new Dictionary<Type, T>();
}
public T Get(int id)
{
var t = typeof(T);
if (!Collection.ContainsKey(t)) return null;
var dict = Collection[t] as Dictionary<int, T>;
if (!dict.ContainsKey(id)) return null;
return (T)dict[id];
}
}
public interface IWorldCollection<T> where T : class
{
T Get(int id);
}
If I have missed something in the requirements, please let me know.
I'm not sure why you need this interface, but maybe this will help:
public class PlayersCollection<T> : IWorldCollection<T> where T:PlayerModel
{
public Dictionary<Type, object> Collection;
public PlayersCollection()
{
Collection = new Dictionary<Type, object>();
}
public T Get(int id)
{
...
}
}
public interface IWorldCollection<T> where T:class
{
T Get(int id);
}
Try this:
public class PlayersCollection : IWorldCollection<PlayerModel>
{
public Dictionary<Type, object> Collection;
public PlayersCollection()
{
Collection = new Dictionary<Type, object>();
}
public PlayerModel Get<PlayerModel>(int id)
{
///
}
}
}
public interface IWorldCollection<T>
{
T Get<T>(int id);
}
In your case, in the class implementing your interface, you adding more condition for your class:
where T : class in the interface
where T: PlayerModel in the class
If, for some reason, you need to add a constraint into your interface, you need to add an interface or base class, which will be placed to the interface declaration, and you'll have to derive from it in your PlayerModel class, like this:
public abstract class Model
{
}
public class PlayerModel : Model
{
}
public interface IWorldCollection<T> where T : Model
{
T Get<T>(int id);
}
public class PlayersCollection : IWorldCollection<PlayerModel>
{
///
public PlayerModel Get<PlayerModel>(int id)
{
///
}
}
}
I have a two tier object hierarchy like so:
public class BaseStat
{
}
public class IndividualDefensiveStat: BaseStat
{
} //and many other stats that inherit from BaseStat
I then have a Generic Repository like so:
public class StatRepository<TDerived> where TDerived: BaseStat
{
//ctr here
public TDerived FindById(int? id)
{
if(!id.HasValue)
throw new Exception("id has to have a value");
var result = _context.Set<TDerived>().Find(id);
return result;
}
public void Insert(TDerived item)
{
_context.Set<TDerived>().Add(item);
_context.SaveChanges();
}
}
Finally I have a service class that accepts a data context, and does CRUD operations based on the derived type passed in:
public class StatDataService
{
private IStatContext _context;
public StatDataService(IStatContext context)
{
_context = context;
}
public void InsertData<TEntity>(TEntity item) where TEntity : BaseStat
{
var repo = getRepository<TEntity>();
repo.Insert(item);
}
public TEntity GetById<TEntity>(int? id) where TEntity : BaseStat
{
return getRepository<TEntity>().FindById(id);
}
private StatRepository<TEntity> getRepository<TEntity>() where TEntity: BaseStat
{
return new StatRepository<TEntity>(_context);
}
}
I would like to have a list of BaseStat that houses items that derive from it. However, if I do a foreach loop and pass each item into the the Service's Insert method, TEntity/TDerived in this case is BaseStat.
How can I have TEntity/TDerived actually be the DerivedType that I am trying to target?
How can I have a type reference that refers to any object that implements a set of interfaces?
For example, I can have a generic type like this:
Java:
public class Foo<T extends A & B> { }
C#
public class Foo<T> where T : A, B { }
That's how to have a class-wide generic type. However, I'd like to simply have a data member which references any object that extends a given set of interfaces.
Example:
public class Foo
{
protected <? extends A, B> object;
public void setObject(<? extends A, B> object)
{
this.object = object;
}
}
If it's possible to have this sort of type syntax, how could I do it in both Java and C#?
I realize I can just create another interface that extends all desired interfaces. However, I don't see this as optimal, as it needlessly adds another type whose sole purpose is to get around syntax. Granted this is a very minor issue, but in terms of elegance it's a bit galling.
My Java has become a bit rusty through 2years of inactivity.
Here's my C# approach: (see https://ideone.com/N20xU for full working sample)
public class Foo
{
private IInterface1 _object; // just pick one
public void setObject<T>(T obj)
where T : IInterface1, IComparable<T>, IEtcetera
{
// you now *know* that object supports all the interfaces
// you don't need the compiler to remind you
_object = obj;
}
public void ExerciseObject()
{
// completely safe due to the constraints on setObject<T>
IEtcetera itf = (IEtcetera) _object;
// ....
}
As far as I know, You cannot create a variable with constraints on it, you can only create a variable of a given type. The type has the constraints. This means you have to define a type that has the constraints you desire, then create the variable with that type.
This seems logical to me, and I don't see why you find it "galling" to have to define a type for what you need.
In C#, you can use a tuple to store the value in a kind of superposition:
public class Foo {
private Tuple<IA, IB> junction;
public void SetValue<T>(T value) where T : IA, IB {
junction = Tuple.Create<IA, IB>(value, value);
}
}
You can also have a specialized class to enforce the constraint that both values reference the same object:
public class Junction {
public IA A { get; private set; }
public IB B { get; private set; }
private Junction() { }
public static Junction Create<T>(T value) where T: IA, IB {
return new Junction {
A = value,
B = value
};
}
}
public class Foo {
private Junction junction;
public void SetValue<T>(T value) where T : IA, IB {
junction = Junction.Create(value);
}
}
In Java, a wildcard would simplify things a little:
class Junction<E extends A & B> {
private final E value;
public Junction(E value) {
this.value = value;
}
public E getValue() {
return value;
}
}
class Foo {
private Junction<?> junction;
public <E extends A & B> void setValue(E value) {
junction = new Junction<E>(value);
}
}
Or you can have aliases to the same value (C#, but also applicable to Java):
public class Foo {
private IA a;
private IB b;
public void SetValue<T>(T value) where T : IA, IB {
a = value;
b = value;
}
}
I don't see any problem with simply stating the constraints as a private interface:
class Foo
{
interface R : I1, I2 { }
R _object;
void setObject(R r) { _object = r; }
}
Here's the best I could come up with (but still not an optimal solution)
public class Foo
{
private TypeWrapper<IInterface1,IInterface2> _object;
public void setObject<T>(T obj)
where T : IInterface1, IInterface2
{
_object = new TypeWrapper<IInterface1, IInterface2>();
_object.SetObject(obj);
var val = _object.Get(h => h.c);
Console.WriteLine(val);
_object.Do(h => h.c = 25);
val = _object.Get(h => h.c);
Console.WriteLine(val);
_object.Do(h => h.someF());
}
}
public class TypeWrapper<TType, TTypeTwo>
{
private Object m_Obj;
public void SetObject<T>(T obj) where T : TType, TTypeTwo
{
m_Obj = obj;
}
public T Get<T>(Func<TType, T> action)
{
return (T)action((TType)m_Obj);
}
public T Get<T>(Func<TTypeTwo, T> action)
{
return (T)action((TTypeTwo)m_Obj);
}
public void Do(Action<TTypeTwo> action)
{
action((TTypeTwo)m_Obj);
}
public void Do(Action<TType> action)
{
action((TType)m_Obj);
}
}
public class myClass : IInterface1, IInterface2 {
public int t {get;set;}
public int c {get;set;}
public void someF() { Console.WriteLine("Fired"); }
}
public interface IInterface1 {
int t { get;set;}
void someF();
}
public interface IInterface2 {
int c { get;set; }
}
You're going to have to work on the object through the Get and Do methods, but it'll work with intellisense and throw compile time errors if the interfaces change.