如何使用泛型方法实现接口
本文关键字:接口 实现 泛型方法 何使用 | 更新日期: 2023-09-27 17:59:52
请考虑以下类和接口:
interface INameable
{
string Name { get; }
}
interface IRepository<T>
{
void Add(T obj);
IEnumerable<T> Values { get; }
}
class Person : INameable
{
public string Name { get; set; }
public int Age { get; set; }
}
class Car : INameable
{
public string Name { get; set; }
public string Model { get; set; }
}
我现在想创建一个同时实现IRepository<Car>
和IRepository<Person>
的存储库类。下面是一个示例实现:
class Repository : IRepository<Car>, IRepository<Person>
{
Dictionary<string, object> values = new Dictionary<string, object>();
void AddValue(INameable o)
{
values.Add(o.Name, o);
}
IEnumerable<T> ValuesOfType<T>()
{
return values.Values.OfType<T>();
}
void IRepository<Car>.Add(Car obj)
{
AddValue(obj);
}
void IRepository<Person>.Add(Person obj)
{
AddValue(obj);
}
IEnumerable<Car> IRepository<Car>.Values
{
get { return ValuesOfType<Car>(); }
}
IEnumerable<Person> IRepository<Person>.Values
{
get { return ValuesOfType<Person>(); }
}
}
这完全符合预期。但是,它非常重复;IRepository<Person>
和 IRepository<Car>
的实现代码几乎完全相同。
我想做的是为T
是INameable
的所有T
实施IRepository
。我试过这个:
class Repository2 : IRepository<Car>, IRepository<Person>
{
// same as before
Dictionary<string, object> values = new Dictionary<string, object>();
void AddValue(INameable o)
{
values.Add(o.Name, o);
}
IEnumerable<T> ValuesOfType<T>()
{
return values.Values.OfType<T>();
}
// using generics to implement both the interfaces
void Add<T>(T obj) where T : INameable
{
AddValue(obj);
}
void Values<T>() where T : INameable
{
return ValuesOfType<T>();
}
}
但是我收到以下错误:
ConsoleApp.Repository2' does not implement interface member 'ConsoleApp.IRepository<ConsoleApp.Car>.Add(ConsoleApp.Car)'
我不确定为什么 Add<T>
和 Vales<T>
方法不匹配 - 两个 T 都可以设置为 Person
和 Car
,然后它们将完全匹配所需的方法类型。
最后,我尝试了:
class Repository3 : IRepository<T> where T is INameable {... }
但是,我收到错误"不允许对非泛型声明进行约束"。
解决这个问题的最佳方法是什么?
请注意,我这样做是为了简单地访问 DbContext
类(该类引用了应用程序中的每个表(,因此我只传递所需的数据,而不是将完整的数据库传递给每个控制器。我这样做是为了更好地将数据库与应用程序的其余部分分开,并提高可测试性。如果有更好的方法可以做到这一点,那也会有所帮助。
你可以有一个带有实现的抽象类,然后只为特定类型继承它。
public interface INameable
{
string Name { get; }
}
public interface IRepository<T>
{
void Add( T obj );
IEnumerable<T> Values { get; }
}
public class Person : INameable
{
public string Name { get; set; }
public int Age { get; set; }
}
public class Car : INameable
{
public string Name { get; set; }
public string Model { get; set; }
}
public abstract class AbstractRepository<T> : IRepository<T>
where T : INameable
{
// same as before
Dictionary<string, object> values = new Dictionary<string, object>();
void AddValue( INameable o )
{
values.Add( o.Name, o );
}
IEnumerable<T> ValuesOfType<T>()
{
return values.Values.OfType<T>();
}
// using generics to implement both the interfaces
public void Add( T obj )
{
AddValue( obj );
}
public IEnumerable<T> Values
{
get
{
return ValuesOfType<T>();
}
}
}
public class CarRepository : AbstractRepository<Car> { }
public class PersonRepository : AbstractRepository<Person> { }
根据我的经验,要添加到某个存储库的所有实体都符合某个接口要容易得多,例如IBaseObject
:
interface IRepository
{
void Add(IBaseObject obj);
IEnumerable<IBaseObject> Values { get; }
}
这通常最终是一个很好的解决方案,因为IBaseObject
您可以拥有一个标识符,以便您知道何时添加或更新现有记录。
更新:
另一种方法是使用以下模式,再次仍然依赖于IBaseObject
:
interface IRepository
{
void Add(T obj) where T : IBaseObject;
IEnumerable<T> GetValues() where T : IBaseObject;
}
我认为,您应该创建创建存储库的类。像这样:
class Repository<T> : IRepository<T>
where T : INameable
{
Dictionary<string, T> values = new Dictionary<string, T>();
void AddValue(T o)
{
values.Add(o.Name, o);
}
public void Add(T obj)
{
AddValue(obj);
}
public IEnumerable<T> Values
{
get { return values.Values; }
}
}
class UnitOfWork
{
private readonly Dictionary<Type, object> _repositories = new Dictionary<Type, object>();
public IRepository<T> GetRepository<T>()
where T : INameable
{
object repository;
if (!_repositories.TryGetValue(typeof (T), out repository))
{
repository = new Repository<T>();
_repositories[typeof (T)] = repository;
}
return (IRepository<T>)repository;
}
}
并像这样使用它:
var unitOfWork = new UnitOfWork();
unitOfWork.GetRepository<Car>().Add(new Car {Name = "Audi"});
unitOfWork.GetRepository<Car>().Add(new Car { Name = "BMW" });
foreach (var car in unitOfWork.GetRepository<Car>().Values)
Console.WriteLine(car.Name);