We have a base class: Filter. DistrFilter and ReportFilter inherit from Filter.
In another class, FilterService.cs, we have two functions that accept these three class types. FilterService operates on Filter objects but it does not inherit from anything.
public class FilterService
{
public string GetDesc(List<T> filters) where T : Filter
{
if(filters.Count == 0) return String.Empty;
StringBuilder s = new StringBuilder("<ul>");
foreach (T f in filters)
s.AppendFormat("<li>{1}</li>", GetFilterText(f));
s.Append("</ul>");
return s.ToString();
}
public string GetFilterText(Filter f)
{
return "filt开发者_StackOverflower";
}
public string GetFilterText(DistrFilter f)
{
return "distr filter";
}
public string GetFilterText(ReportFilter f)
{
return "report filter";
}
}
public static void main(string[] args)
{
List<DistrFilter> distrFilters = new List<DistrFilters>();
distrFilters.Add(new DistrFilter());
distrFilters.Add(new DistrFilter());
distrFilters.Add(new DistrFilter());
FilterService fs = new FilterService();
Console.WriteLine(fs.GetDescription(distrFilters));
}
Then it prints:
- filter
- filter ...
How do I get it to print this instead?
- distr filter
- distr filter ...
Add a virtual method to the Filter
class, called GetName()
or something similar, and implement it as return "distr filter";
in DistrFilter
and as return "report filter";
in ReportFilter
. Then just call f.GetName()
in GetDesc()
.
Alternatively, you could use checks like if (f is DistrFilter)
in GetDesc()
, but that sort of structure, explicitly checking for specific derived classes and handling them differently, is generally considered poor design.
Either implement GetFilterText()
as a virtual method in Filter
:
class Filter
{
// Can be converted into a property as well.
public virtual string GetFilterText { return "filter"; }
}
class DistrFilter : Filter
{
public override string GetFilterText { return "distr filter"; }
}
Then do this:
StringBuilder s = new StringBuilder("<ul>");
foreach (T f in filters)
s.AppendFormat("<li>{0}</li>", f.GetFilterText());
Or if you want to separate the concern of specifying the filter text from the Filter
classes, use double dispatch (visitor pattern). This is useful if you can have different types of filter services. This can be done this way:
interface IServiceAcceptor
{
string Accept(FilterService service);
}
public class Filter : IServiceAcceptor
{
string IServiceAcceptor.Accept(FilterService service)
{
return service.GetFilterText(this);
}
}
public class DistrFilter : Filter, IServiceAcceptor
{
string IServiceAcceptor.Accept(FilterService service)
{
return service.GetFilterText(this);
}
}
public class ReportFilter : Filter, IServiceAcceptor
{
string IServiceAcceptor.Accept(FilterService service)
{
return service.GetFilterText(this);
}
}
Then in your service:
public string GetDesc<T>(List<T> filters) where T : IServiceAcceptor
{
if(filters.Count == 0) return String.Empty;
var s = new StringBuilder("<ul>");
foreach (T f in filters)
s.AppendFormat("<li>{0}</li>", f.Accept(this));
s.Append("</ul>");
return s.ToString();
}
Some references: Double dispatch, Visitor Pattern
Implement a pattern that does something like this:
public class Filter
{
public virtual string GetDescription()
{
return "filter";
}
}
public class DistrFilter : Filter
{
public override string GetDescription()
{
return "distr filter";
}
}
public class ReportFilter : Filter
{
public override string GetDescription()
{
return "report filter";
}
}
public class FilterService
{
public string GetDescription<T>( List<T> filters )
where T: Filter
{
if ( filters.Count == 0 )
return String.Empty;
StringBuilder s = new StringBuilder( "<ul>" );
foreach ( T f in filters )
s.AppendFormat( "<li>{0}</li>", f.GetDescription() );
s.Append( "</ul>" );
return s.ToString();
}
}
精彩评论