C# 如何设置位于通用集合对象之上的通用分页对象?
我一直在努力从我的应用程序中删除大量重复的代码,特别是在我的模型中。一些模型还有一个集合变量,它是模型类型的IEnumerable。以前所有的集合变体都是单独的实现,但我能够将它们的大部分代码合并到ModelCollection基类中 现在,在这些集合模型之上是具有分页值的其他模型,它们都具有完全相同的属性,因此我还想将它们折叠成一个基类。我遇到的问题是.NET不支持多重继承,而且由于每个ModelCollection实现仍然需要显式实现,因为大多数实现都有一些特殊的逻辑,因此简单的泛型链也不能解决这个问题 ModelCollection基类:C# 如何设置位于通用集合对象之上的通用分页对象?,c#,generics,multiple-inheritance,mixins,castle-dynamicproxy,C#,Generics,Multiple Inheritance,Mixins,Castle Dynamicproxy,我一直在努力从我的应用程序中删除大量重复的代码,特别是在我的模型中。一些模型还有一个集合变量,它是模型类型的IEnumerable。以前所有的集合变体都是单独的实现,但我能够将它们的大部分代码合并到ModelCollection基类中 现在,在这些集合模型之上是具有分页值的其他模型,它们都具有完全相同的属性,因此我还想将它们折叠成一个基类。我遇到的问题是.NET不支持多重继承,而且由于每个ModelCollection实现仍然需要显式实现,因为大多数实现都有一些特殊的逻辑,因此简单的泛型链也不能
public abstract class ModelCollection<TModel> : IEnumerable<T>, IModel where TModel : IPersistableModel
{
protected readonly List<TModel> Models;
protected ModelCollection()
{
Models = new List<TModel>();
}
protected ModelCollection(params TModel[] models)
: this((IEnumerable<TModel>)models)
{
}
protected ModelCollection(IEnumerable<TModel> models)
: this()
{
models.ForEach(Models.Add);
}
public virtual int Count
{
get { return Models.Count; }
}
public virtual IEnumerator<TModel> GetEnumerator()
{
return Models.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public virtual void Add(TModel model)
{
Models.Add(model);
}
public virtual void Accept(Breadcrumb breadcrumb, IModelVisitor visitor)
{
foreach (var model in this)
{
model.Accept(breadcrumb.Attach("Item"), visitor);
}
visitor.Visit(breadcrumb, this);
}
public bool IsSynchronized { get; set; }
}
IModel接口供参考:
public interface IModel
{
void Accept(Breadcrumb breadcrumb, IModelVisitor visitor);
}
可以使用Castle.DynamicProxy及其Mixins功能解决此问题 首先,ModelCollection基类需要有一个接口,在这个特定示例中,由于实现只是IModel和IEnumerable接口的组合,它只需要继承这些接口:
public interface IModelCollection<T> : IEnumerable<T>, IModel
{
}
public interface IPagedCollection<T> : IPagingDetails, IModelCollection<T>
{}
其次,您需要一个接口和一个实现来表示分页字段:
public interface IPagingDetails
{
int CurrentPage { get; set; }
int TotalPages { get; set; }
int TotalCount { get; set; }
bool HasPreviousPage { get; }
int PreviousPage { get; }
bool HasNextPage { get; }
int NextPage { get; }
}
public class PagingDetails : IPagingDetails
{
public int CurrentPage { get; set; }
public int TotalPages { get; set; }
public int TotalCount { get; set; }
public bool HasPreviousPage
{
get { return CurrentPage > 1; }
}
public int PreviousPage
{
get { return CurrentPage - 1; }
}
public bool HasNextPage
{
get { return CurrentPage < TotalPages; }
}
public int NextPage
{
get { return CurrentPage + 1;}
}
}
接下来,为了简化代理生成后对各个部分的访问,特别是为了消除在ModelCollection和PagingDetails接口之间强制转换代理的要求,我们需要一个空接口来组合paging和collections接口:
public interface IModelCollection<T> : IEnumerable<T>, IModel
{
}
public interface IPagedCollection<T> : IPagingDetails, IModelCollection<T>
{}
最后,我们需要将分页详细信息作为mixin创建代理:
public class PagedCollectionFactory
{
public static IPagedCollection<TModel> GetAsPagedCollection<TModel, TCollection>(int currentPage, int totalPages, int totalCount, TCollection page)
where TCollection : IModelCollection<TModel>
where TModel : IPersistableModel
{
var generator = new ProxyGenerator();
var options = new ProxyGenerationOptions();
options.AddMixinInstance(new PagingDetails { CurrentPage = currentPage, TotalPages = totalPages, TotalCount = totalCount });
return (IPagedCollection<TModel>)generator.CreateClassProxyWithTarget(typeof(TCollection), new[] { typeof(IPagedCollection<TModel>) }, page, options);
}
}
现在我们可以使用代理,这是从我编写的单元测试中获取的,以确保它能够工作:
var collection = new NoteCollection(
new Note {Text = "note 1"},
new Note {Text = "note 2"},
new Note {Text = "note 3"},
new Note {Text = "note 4"});
var pagedCollection = PagedCollectionFactory.GetAsPagedCollection<Note,NoteCollection>(1, 1, 1, collection);
Assert.That(pagedCollection.CurrentPage, Is.EqualTo(1));
Assert.That(pagedCollection.ToList().Count, Is.EqualTo(4));
如果您对问题、答案或标题中的措辞有任何改进建议,请毫不犹豫地进行评论或编辑。这是一个如此复杂的问题/解决方案,我想确保一切都非常清楚。