C#泛型-根据对象类型查找正确的具体类
我的域模型中有几个新闻类型,它们是NewsItem的子类,如下所示(简化): 以下是NewsItem的几个子类:C#泛型-根据对象类型查找正确的具体类,c#,generics,reflection,inheritance,interface,C#,Generics,Reflection,Inheritance,Interface,我的域模型中有几个新闻类型,它们是NewsItem的子类,如下所示(简化): 以下是NewsItem的几个子类: public class NewsItemJoiner : NewsItem { public virtual Account AccountJoined { get; set; } } public class NewsItemStatus : NewsItem { public virtual string Status { get; set; } } 在我的
public class NewsItemJoiner : NewsItem
{
public virtual Account AccountJoined { get; set; }
}
public class NewsItemStatus : NewsItem
{
public virtual string Status { get; set; }
}
在我的MVC应用程序中,我想返回一个Newsitem的集合,其中可能包含许多不同的Newsitem子类。我现在需要做的是循环遍历每个新闻项,并从相关类中为特定类型的新闻项调用一个呈现函数……代码可能会解释得更简单一些:
public interface IRenderer<T> where T : NewsItem
{
string Render(T item);
}
public class JoinedRenderer : IRenderer<NewsItemJoiner>
{
public string Render(NewsItemJoiner item)
{
return String.Format("{0} has just joined our music network.", item.AccountJoined.ArtistName);
}
}
public class StatusUpdateRenderer : IRenderer<NewsItemStatus>
{
public string Render(NewsItemStatus item)
{
return String.Format("<span class='statusupdate'>{0}<span>", item.Status);
}
}
公共接口iRender,其中T:NewsItem
{
字符串渲染(T项);
}
公共类联合招标人:iEnder
{
公共字符串呈现(NewsItemJoiner项)
{
返回String.Format(“{0}刚刚加入我们的音乐网络。”,item.AccountJoined.ArtistName);
}
}
公共类StatusUpdaterEnder:iEnder
{
公共字符串呈现(NewsItemStatus项)
{
返回String.Format(“{0}”,item.Status);
}
}
我需要根据新闻项的类型以某种方式调用正确的类呈现函数。您可以制作一个字典,将新闻项的类型用作键,将呈现函数用作值。或者,您可以维护具有渲染函数的所有类的列表,或者仅维护所有渲染函数的列表,并使用反射来确定应该使用哪个方法。然而,在我看来,代替做任何这些,你应该考虑重新设计你的应用程序,以便NeXITEM抽象类本身有一个虚拟的渲染函数。这将大大简化您的任务
编辑:以前认为NewsItem是一个接口。您可以制作一个字典,使用NewsItem的类型作为键,使用Render函数作为值。或者,您可以维护具有渲染函数的所有类的列表,或者仅维护所有渲染函数的列表,并使用反射来确定应该使用哪个方法。然而,在我看来,代替做任何这些,你应该考虑重新设计你的应用程序,以便NeXITEM抽象类本身有一个虚拟的渲染函数。这将大大简化您的任务
编辑:以前认为NewsItem是一个界面。对于虚拟功能来说,这似乎是一个相当明显的例子
public abstract class RenderableNewsItem : NewsItem
{
abstract public string Render();
}
public class NewsItemStatus : RenderableNewsItem
{
public virtual string Status { get; set; }
public string Render()
{
return String.Format("<span class='statusupdate'>{0}<span>", this.Status);
}
}
公共抽象类RenderableNewsItem:NewsItem
{
抽象公共字符串Render();
}
公共类NewsItemStatus:RenderableNewsItem
{
公共虚拟字符串状态{get;set;}
公共字符串呈现()
{
返回String.Format(“{0}”,this.Status);
}
}
对于虚拟函数来说,这似乎是一个相当明显的例子
public abstract class RenderableNewsItem : NewsItem
{
abstract public string Render();
}
public class NewsItemStatus : RenderableNewsItem
{
public virtual string Status { get; set; }
public string Render()
{
return String.Format("<span class='statusupdate'>{0}<span>", this.Status);
}
}
公共抽象类RenderableNewsItem:NewsItem
{
抽象公共字符串Render();
}
公共类NewsItemStatus:RenderableNewsItem
{
公共虚拟字符串状态{get;set;}
公共字符串呈现()
{
返回String.Format(“{0}”,this.Status);
}
}
一种可能性:启动时(即在与呈现代码相关的静态构造函数中),迭代程序集中的类,实例化并存储
IRenderer
的字典,实现映射到它们呈现的类型的实例
(此建议假设渲染器对象是线程安全的,因为您可能会一次从多个请求线程调用Render
方法。如果它们不是线程安全的,则需要将字典更改为
,并为每次使用实例化渲染器。)
例如:
public class RenderUtil
{
static Dictionary<Type, object> s_renderers;
static RenderUtil()
{
s_renderers = new Dictionary<Type, object>();
foreach (var type in Assembly.GetExecutingAssembly().GetTypes())
{
var renderInterface = type.GetInterfaces().FirstOrDefault(
i => i.IsGenericType &&
i.GetGenericTypeDefinition() == typeof(IRenderer<>));
if (renderInterface != null)
{
s_renderers.Add(
renderInterface.GetGenericArguments()[0],
Activator.CreateInstance(type));
}
}
}
public static string Render<T>(T item)
{
IRenderer<T> renderer = null;
try
{
// no need to synchronize readonly access
renderer = (IRenderer<T>)s_renderers[item.GetType()];
}
catch
{
throw new ArgumentException("No renderer for type " + item.GetType().Name);
}
return renderer.Render(item);
}
}
请注意,RenderUtil
类将在第一次使用时通过TypeInitializationException
抛出一个DuplicateKeyException
,如果给定类型有多个呈现器。一种可能性:启动时(即在与呈现代码相关的静态构造函数中),迭代程序集中的类,实例化并存储irender
的字典-实现映射到它们呈现的类型的实例
(此建议假设渲染器对象是线程安全的,因为您可能会一次从多个请求线程调用Render
方法。如果它们不是线程安全的,则需要将字典更改为
,并为每次使用实例化渲染器。)
例如:
public class RenderUtil
{
static Dictionary<Type, object> s_renderers;
static RenderUtil()
{
s_renderers = new Dictionary<Type, object>();
foreach (var type in Assembly.GetExecutingAssembly().GetTypes())
{
var renderInterface = type.GetInterfaces().FirstOrDefault(
i => i.IsGenericType &&
i.GetGenericTypeDefinition() == typeof(IRenderer<>));
if (renderInterface != null)
{
s_renderers.Add(
renderInterface.GetGenericArguments()[0],
Activator.CreateInstance(type));
}
}
}
public static string Render<T>(T item)
{
IRenderer<T> renderer = null;
try
{
// no need to synchronize readonly access
renderer = (IRenderer<T>)s_renderers[item.GetType()];
}
catch
{
throw new ArgumentException("No renderer for type " + item.GetType().Name);
}
return renderer.Render(item);
}
}
请注意,RenderUtil
类将在第一次使用时通过TypeInitializationException
抛出一个DuplicateKeyException
,如果给定类型有多个呈现器。请考虑反转控制逻辑,并在NewsItem中提供一个虚拟呈现()方法。例如
abstract class NewsItem {
// ...
public virtual string Render() { return string.Empty; }
}
然后您的子类可以根据需要实现:
public class NewsItemJoiner : NewsItem
{
// ...
public override string Render() {
return String.Format("{0} has just joined our music network.", this.AccountJoined.ArtistName);
}
}
编辑:
替代技术
从他人的意见中得出的观点重新分离了关注点。我不知道你是否因为其他原因被设置在iRender上,但是如果你没有,还有一种技术不需要使用反射。您可以改用访问者模式
首先声明NewsItemVisitor类:
public abstract class NewsItemVisitor
{
public abstract void Visit(NewsItemJoiner joiner);
public abstract void Visit(NewsItemStatus status);
}
接下来,向NewsItem添加一个virtual Accept()方法(在本例中,我将您的数据类型更改为string,而不是Account、Status等):
现在您可以创建一个具体的访问者,它是我们的渲染器:
public class NewsItemListRenderer : NewsItemVisitor
{
private readonly List<NewsItem> itemList;
private string renderedList = string.Empty;
public NewsItemListRenderer(List<NewsItem> itemList)
{
this.itemList = itemList;
}
public string Render()
{
foreach (var item in itemList)
{
item.Accept(this);
}
return renderedList;
}
public override void Visit(NewsItemJoiner joiner)
{
renderedList += "joiner: " + joiner.AccountJoined + Environment.NewLine;
}
public override void Visit(NewsItemStatus status)
{
renderedList += "status: " + status.Status + Environment.NewLine;
}
}
考虑反转控制逻辑,并在NewsItem中提供一个virtual Render()方法。例如
abstract class NewsItem {
// ...
public virtual string Render() { return string.Empty; }
}
然后您的子类可以根据需要实现:
public class NewsItemJoiner : NewsItem
{
// ...
public override string Render() {
return String.Format("{0} has just joined our music network.", this.AccountJoined.ArtistName);
}
}
编辑:
替代技术
从他人的意见中得出的观点重新分离了关注点。我不知道你是否因为其他原因被设置在iRender上,但是如果你没有,还有一种技术不需要使用反射。您可以改用访问者模式
首先声明NewsItemVisitor类:
public abstract class NewsItemVisitor
{
public abstract void Visit(NewsItemJoiner joiner);
public abstract void Visit(NewsItemStatus status);
}
接下来,向NewsItem添加一个virtual Accept()方法(对于本例,I
List<GenericContainer> gcList = new List<GenericContainer>();
// GenericContainer can be a Jug, Bottle, Barrel, or just a GenericContainer type
// [..fill it..]
GenericContainer gc = gcList[i];
Object returnvalue = gc.GetType()
.GetMethod("Pour", BindingFlags.Instance).Invoke(gc, null);
<% foreach (var newsItem in Model.NewsItems) { %>
<%= Html.RenderPartial(newsItem.PartialViewName, newsItem) %>
<% } >