generics interface constraint in c# - c#

I've written a code as below. In this code I want to put a constraint on ServiceResult and BaseService classes so that T needs to implement IBaseEntity interface.
Here is the code:
public interface IBaseEntity
{
int Id { get; set; }
}
public class Photo : IBaseEntity
{
public int Id { get; set; }
public float FileSize { get; set; }
public string Url { get; set; }
}
public class ServiceResult<T> where T : class, IBaseEntity, new()
{
public bool Succeed { get; set; }
private T data;
public T Data
{
get
{
if (data == null)
data = new T();
return data;
}
set
{
data = value;
}
}
}
public abstract class BaseService<T> where T : class, IBaseEntity, new()
{
public abstract ServiceResult<List<T>> GetAll();
public abstract ServiceResult<T> GetById(int Id);
}
public class PhotoService : BaseService<Photo>
{
public override ServiceResult<List<Photo>> GetAll()
{
throw new Exception();
}
public override ServiceResult<Photo> GetById(int Id)
{
throw new Exception();
}
}
public class Program
{
public static void Main(string[] args)
{
Console.ReadKey();
}
}
In the code I get the error as below (error refers to GetAll() methods)
Error 3 The type
'System.Collections.Generic.List' cannot be used
as type parameter 'T' in the generic type or method
'FOC.Session04.ServiceResult'. There is no implicit reference
conversion from 'System.Collections.Generic.List'
to 'FOC.Session04.IBaseEntity'. G:\Courses\ASP.NET MVC5\Session4
960803\FOC.Session04\FOC.Session04\Program.cs 55 52 FOC.Session04`
But when I remove the interface constraint IBaseEntity from ServiceResult class and let it remain after BaseService I will get no error and the code compiles without error.
Can anybody explain me why I can't add constraint after ServiceResult class?
What's the reason? Or which part of code need to be changed in order to compile error less in this case?
Thanks all

I think you want rather
public abstract List<ServiceResult<T>> GetAll();
instead of
public abstract ServiceResult<List<T>> GetAll();
List<T> does not even match your constraints (hence the compiler error)

What you really need is for GetAll to return a List of ServiceResults, like so
public interface IBaseEntity
{
int Id { get; set; }
}
public class Photo : IBaseEntity
{
public int Id { get; set; }
public float FileSize { get; set; }
public string Url { get; set; }
}
public class ServiceResult<T> where T : class, IBaseEntity, new()
{
public bool Succeed { get; set; }
private T data;
public T Data
{
get
{
if (data == null)
data = new T();
return data;
}
set
{
data = value;
}
}
}
public abstract class BaseService<T> where T : class, IBaseEntity, new()
{
public abstract List<ServiceResult<T>> GetAll();
public abstract ServiceResult<T> GetById(int Id);
}
public class PhotoService : BaseService<Photo>
{
public override List<ServiceResult<Photo>> GetAll()
{
throw new Exception();
}
public override ServiceResult<Photo> GetById(int Id)
{
throw new Exception();
}
}
public class Program
{
public static void Main(string[] args)
{
Console.ReadKey();
}
}

I made a look at the source again and understood what is going on.
Putting IBaseEntity constraint on BaseService class has no problem. Because T represents a single class here (PhotoService : BaseService<Photo>). So T is Photo and Photo implements IBaseEntity.
But for GetAll() method in BaseService class the return type is ServiceResult<List<T>>. Therefore in ServiceResult class T will be something like List<Photo> and List<Photo> doesn't implement IBaseEntity. Therefore it raises an error.
Removing IBaseEntity constraint from ServiceResult class solves the problem.

Related

return a list of generic C#

I have a singleton class, holding several list of data. I want to have a function to return one of the list based on the requested data type
public interface IRentable
{
long Id { get; set; }
}
public class Book : IRentable
{
private long _id;
public long Id
{
get { return _id; }
set { _id = value; }
}
public string Name { get; set; }
public int Pages { get; set; }
}
public class DVD : IRentable
{
private long _id;
public long Id
{
get { return _id; }
set { _id = value; }
}
public string Name { get; set; }
public TimeSpan Length { get; set; }
}
public class DBReferenceSingleton
{
private List<Book> _bookList;
private List<DVD> _dvdList;
public IEnumerable<Entity> GetEntities<Entity>() where Entity : IRentable
{
switch(typeof(Entity).Name)
{
case nameof(Book):
return _bookList;
case nameof(DVD):
return _dvdList;
}
return null;
}
This is not working, since a conversion is needed. I wonder if there is any elegant solution (without serialize all elements or implement IConvertible)?
Background: I was looking into the implementation of Repository pattern implementation from Microsoft document website and a couple of YouTube videos. Their base class implementations are bounded with DBContext,
[from Microsoft doc]
public class GenericRepository<TEntity> where TEntity : class
{
internal SchoolContext context;
internal DbSet<TEntity> dbSet;
[from youtube]
public class Repository<TEntity> : IRepository<TEntity> where TEntity : class
{
protected readonly DbContext Context;
public Repository(DbContext context)
{
Context = context;
}
public IEnumerable<TEntity> GetAll()
{
return Context.Set<TEntity>().ToList();
}
So, this means this base class cannot be used for databases without EntityFramework. I'm trying to remove DBContext in my base class
public class InMemoryRepository<TEntity> : IRepository<TEntity> where TEntity : IRentable
{
private IEnumerable<TEntity> _entities;
public IEnumerable<TEntity> GetAll()
{
if (_entities == null)
{
_entities = DBReferenceSingleton.GetInstance.GetEntities<TEntity>();
}
return _entities;
}
public TEntity Get(long id)
{
GetAll();
if (_entities == null)
{
return default(TEntity);
}
return _entities.FirstOrDefault(t => t.Id == id);
}
(I was wrong in my previous code, as I was using "class" instead of "IRentable" as I thought it will be generic for all classes. After implementing the Get(long id) function. I found I have to have a IRentable interface so I made the changes. And the answer from The Lemon work perfectly.
Thanks
It doesn't look like generics seem the right tool here; not least because Book and DVD don't have a common base class.
I think you should have two methods:
public List<Book> GetBooks() => _bookList;
public List<DVD> GetDvds() => _dvdList;
Have you tried doing a Cast?
public class Book
{
public string Name { get; set; }
public int Pages { get; set; }
}
public class DVD
{
public string Name { get; set; }
public TimeSpan Length { get; set; }
}
public class DBReferenceSingleton
{
private List<Book> _bookList;
private List<DVD> _dvdList;
public IEnumerable<Entity> GetEntities<Entity>() where Entity : class
{
switch(typeof(Entity).Name)
{
case nameof(Book):
return _bookList as List<Entity>;
case nameof(DVD):
return _dvdList as List<Entity>;
}
return null;
}
}
the "as List< Entity >" will return null if the object is the wrong type, or the object as the type if it's the right type. The A = B as C pattern works for inherited types as well, just remember to check for null returned values for cases where your type isn't as well known as in this situation
A side comment on the usefulness of having a generic method in this case: In this method you're forced to set the type of entity each time explicitly, which means your method is functionally non-generic - so you might as well make two explicit methods.
One case where a generic method like the one you have might be more useful, is if book and dvd both inherited from a base class, and you had some follow up methods that needed to operate off a list of . For example, you might end up wanting to do something like this instead in your code:
public class Book : Rentable
{
public int Pages { get; set; }
}
public class DVD : Rentable
{
public TimeSpan Length { get; set; }
}
public class Rentable
{
public string Name { get; set; }
public string borrowedBy { get; set; }
}
public class DBReferenceSingleton
{
private List<Book> _bookList;
private List<DVD> _dvdList;
public enum RentableType { Book, DVD }
public IEnumerable<Rentable> GetEntities(RentableType entityType)
{
switch (entityType)
{
case RentableType.Book:
return _bookList.ToList<Rentable>();
case RentableType.DVD:
return _dvdList.ToList<Rentable>();
default:
throw new NotImplementedException($"Entity {entityType} not supported");
}
return null;
}
}

Implement an interface method with a concrete class

I have the following interfaces, one for the entity and one for some logic:
public interface IItem
{
int Id { get; set; }
}
public interface IGenerator
{
IList<IItem> Generate();
}
and implementation:
public class ItemA : IItem
{
public int Id { get; set; }
public string Name { get; set; }
}
public class ItemAGenerator : IGenerator
{
public IList<ItemA> Generate()
{
// do stuff
return List<ItemA>;
}
}
That implementation did not work, it says that it does not have the matching return type, so I also tried:
public class ItemAGenerator : IGenerator
{
public IList<IItem> Generate()
{
// do stuff
return List<ItemA>;
}
}
it does not work as well, it says: cannot implicitly convert type List<IItem> to List<ItemA>.
How to make it work? what am I missing here.
Just create the list as a List<IItem> but add ItemA's to it.
public class ItemAGenerator : IGenerator
{
public IList<IItem> Generate()
{
var list = new List<IItem>();
list.Add(new ItemA());
return list;
}
}
Just make IGenerator generic. Then you can specify the type that will be returned.
public interface IGenerator<T> where T : IItem
{
IList<T> Generate();
}
public class ItemAGenerator : IGenerator<ItemA>
{
public IList<ItemA> Generate()
{
// do stuff
return List<ItemA>;
}
}

Generic Abstract Class

I have the following code which is fine...
namespace GenericAbstract
{
public interface INotifModel
{
string Data { get; set; }
}
public interface INotif<T> where T: INotifModel
{
T Model { get; set; }
}
public interface INotifProcessor<in T> where T : INotif<INotifModel>
{
void Yell(T notif);
}
public class HelloWorldModel : INotifModel
{
public string Data { get; set; }
public HelloWorldModel()
{
Data = "Hello world!";
}
}
public class HelloWorldNotif : INotif<HelloWorldModel>
{
public HelloWorldModel Model { get; set; }
public HelloWorldNotif()
{
Model = new HelloWorldModel();
}
}
public class HelloWorldProcessor<T> : INotifProcessor<T> where T : INotif<INotifModel>
{
public void Yell(T notif)
{
throw new NotImplementedException();
}
}
}
As you can see there are 3 interfaces and each of those is implemented.
However, I would like the processor to be implemented like this:
public class HelloWorldProcessor : INotifProcessor<HelloWorldNotif<HelloWorldModel>>
{
public void Yell(HelloWorldNotif<HelloWorldModel> notif)
{
throw new NotImplementedException();
}
}
But i get the following error:
The non-generic type 'HelloWorldNotif' cannot be used with type arguments
I want the HelloWorldProcessor to implement INotifProcessor only for HelloWorldNotif...
Can't figure out what I am doing wrong..
For this to work you first have to make INotif<T> co-variant. That means that the Model property has to be read only for the interface (it can still have a public set in an implementation). Then to fix your immediate error you don't put the <HelloWorldModel> after HelloWorldNotif because it's already a INotif<HelloWorldModel>
public interface INotifModel
{
string Data { get; set; }
}
public interface INotif<out T> where T : INotifModel
{
T Model { get; }
}
public interface INotifProcessor<in T> where T : INotif<INotifModel>
{
void Yell(T notif);
}
public class HelloWorldModel : INotifModel
{
public string Data { get; set; }
public HelloWorldModel()
{
Data = "Hello world!";
}
}
public class HelloWorldNotif : INotif<HelloWorldModel>
{
public HelloWorldModel Model { get; set; }
public HelloWorldNotif()
{
Model = new HelloWorldModel();
}
}
public class HelloWorldProcessor<T> : INotifProcessor<T> where T : INotif<INotifModel>
{
public void Yell(T notif)
{
throw new NotImplementedException();
}
}
public class HelloWorldProcessor : INotifProcessor<HelloWorldNotif>
{
public void Yell(HelloWorldNotif notif)
{
throw new NotImplementedException();
}
}
Then I guess your implementation would be something like
Console.WriteLine(notif.Model.Data);
As others have said and/or implied out you've already got HelloWorldNotif fully specified. So to translate this:
I want the HelloWorldProcessor to implement INotifProcessor only for
HelloWorldNotif
To C#, I think you mean:
public class HelloWorldProcessor : INotifProcessor<HelloWorldNotif>
{
public void Yell(HelloWorldNotif notif)
{
throw new NotImplementedException();
}
}

Generic abstract builder properties needs casting

I've build a generic (abstract) builder which will provide basic implementation
for entity builders that will be used during testing.
This is the entity base class:
public abstract class Entity : IObjectState
{
[NotMapped]
public ObjectState ObjectState { get; set; }
}
This is the IKey interface:
public interface IKey
{
int Id { get; set; }
}
This is the Builder class:
public abstract class Builder<T> where T : Entity, IKey, new()
{
protected int _id { get; set; }
protected ObjectState _objectState { get; set; }
public Builder()
{
_objectState = ObjectState.Added;
}
public virtual Builder<T> WithId(int id)
{
this._id = id;
return this;
}
public virtual Builder<T> HavingObjectState(ObjectState objectState)
{
_objectState = objectState;
return this;
}
public static implicit operator T(Builder<T> builder)
{
return new T
{
Id = builder._id,
ObjectState = builder._objectState
};
}
}
This is a sample UnitBuilder implementation:
public class UnitBuilder : Builder<Unit>
{
private string _shortDescription;
private string _longDescription;
public UnitBuilder WithShort(string shortDescription)
{
_shortDescription = shortDescription;
return this;
}
public UnitBuilder WithLong(string longDescription)
{
_longDescription = longDescription;
return this;
}
public static implicit operator Unit(UnitBuilder builder)
{
return new Unit
{
Id = builder._id,
ObjectState = builder._objectState,
Short = builder._shortDescription,
Long = builder._longDescription
};
}
}
And this is the problem I'm having:
The error:
Error CS1061 'Builder' does not contain a definition for
'WithShort' and no extension method 'WithShort' accepting a first
argument of type 'Builder' could be found (are you missing a
using directive or an assembly reference?)
I understand what is going on but I would like a better (more elegant) solution than thirdUnit.
UPDATE:
As per suggestion I added the following to the UnitBuilder class:
public new UnitBuilder WithId(int id)
{
return (UnitBuilder)base.WithId(id);
}
public new UnitBuilder WithObjectState(ObjectState objectState)
{
return (UnitBuilder)base.WithObjectState(objectState);
}
But now I don't see any point in the base class... This has to be a
general generic base class problem, how do other people handle this?
Maybe the thirdUnit solution IS elegant but I'm just being difficult about it? :)
The answer is simple, your base class builder methods have to be called last and cannot be chained with your more specific builder classes as it returns the generic. So simply change your code to:
Unit secondUnit = new UnitBuilder()
.WithShort("ShortDesc")
.WithId(10);
That's it!
This is my working final solution:
public abstract class Builder<TEntity, TBuilder>
where TEntity : Entity, IKey, new()
where TBuilder : Builder<TEntity, TBuilder>, new()
{
protected int _id { get; set; }
protected ObjectState _objectState { get; set; }
public Builder()
{
_objectState = ObjectState.Added;
}
public virtual Builder<TEntity, TBuilder> WithId(int id)
{
this._id = id;
return this;
}
public virtual Builder<TEntity, TBuilder> WithObjectState(ObjectState objectState)
{
this._objectState = objectState;
return this;
}
public static implicit operator TEntity(Builder<TEntity, TBuilder> builder)
{
return new TEntity
{
Id = builder._id,
ObjectState = builder._objectState
};
}
}

C# MVC generic repository with custom column name

How can I achieve something like the following?
public interface IGenericRepository
{
int id { get; }
T GetById<T>() where T : class
}
public class GenericRepository : IGenericRepository
{
//Some code here
public T GetById<T>(int tid) where T : class
{
return from tbl in dataContext.GetTable<T> where tbl.id == tid select tbl;
}
}
And I would like to use this as follows:
GenericRepository gr = new GenericRepository();
Category cat = gr.GetById<Category>(15);
Of course, in this usage, tbl.id in the GenericRepository gives me an error.
SOLUTION
public class IHasId
{
public int id { get; set; }
}
public interface IGenericRepository
{
int id { get; }
T GetById<T>(int id) where T : IHasId;
}
public class GenericRepository : IGenericRepository
{
public int id
{
get { throw new NotImplementedException(); }
}
public T GetById<T>(int id) where T : IHasId
{
return from tbl in dataContext.GetTable<T> where tbl.id == tid select tbl;
}
}
And apart from these, DON'T forget to define this somewhere in your model:
public partial class Category : IHasId { }
And the usage is:
Repository rep = new Repository();
Category cat = rep.GetById<Category>(15);
There's a few problems here - the first is that the generic type you're matching is a class, but a class doesn't have a property called 'id'. You need to have your Category class implement an interface that exposes an 'id' property:
public interface IIdentity
{
int identity { get; set; }
}
public class Category : IIdentity
{
public int identity{ get; set; }
}
I don't know why you've exposed 'id' as a property on the IGenericRepository interface - surely this is supposed to be a parameter passed to the find method (as indicated by your implementation). You also need to change the restriction on the 'GetById' method from:
where T : class
to something like
where T : IIdentity
using the interface I've suggested above.
public class IHasId
{
public int id { get; set; }
}
public interface IGenericRepository<T>
{
int id { get; }
T GetById(int id);
}
public class GenericRepository<T> : IGenericRepository<T> where T : IHasId
{
public int id
{
get { throw new NotImplementedException(); }
}
public T GetById(int id)
{
return from tbl in dataContext.GetTable<T> where tbl.id == tid select tbl;
}
}
You get this error because you accept every class where T : class. A class don't have that property.
Create an abstract class or interface to make sure that this property exists and change where T : class to where T : IHasIdProperty.

Categories

Resources