Reputation: 5242
I have a base class called Base
. This base class has a method defined as:
protected virtual string GetFormattedAttribute(string propertyName, object propertyValue)
The propertyValue
is defined as:
dynamic propertyValue
So different type of values are assigned to it and appropriate methods are called automatically. For example, I have another virtual method in the same base class for DateTime
as:
protected virtual string GetFormattedAttribute(string propertyName, DateTime propertyValue)
This works perfectly. Now, I have a dervied class in which I define the same method, which accepts a List<Contact>
:
protected string GetFormattedAttribute(string propertyName, List<Contact> contacts)
This, however, is never called and the first method with propertyValue
as object
in Base
is called instead (I'm calling this method with a derived class object). I tried new
and override
keywords in the derived class method, but that doesn't work. Any ideas how can I achieve this result?
Upvotes: 2
Views: 200
Reputation: 6626
Seems to me that you could be using generics to achieve what you want.
So In base you would have
protected virtual string GetFormattedAttribute<T>(string propertyName, T propertyValue)
This would be useful if all types have some sort of common generic formatting.
That seems nice on initial inspection, but what if your types do not have some generic formatting pattern (which I assume to be the case). Rather than implementing all the formatting in one single class, I would much rather devide and conquer, implementing some small dedicated formatted classes tightly bound to the type they are formatting. Such classes would implement the generic interface IPropertyFormatter<T>
public interface IPropertyFormatter<T> {
string FormatValue(T value);
}
The most generic class that would need to implement this interface is simply when you do for the Object.ToString()
to get the value, hence we have the ObjectPropertyFormatter as the most generic implementation of IPropertyFormatter
public class ObjectPropertyFormatter : IPropertyFormatter<Object>
{
public string FormatValue(Object value)
{
//object fallback formatting logic
return value.ToString();
}
}
Now suppose some types need special handling. Then we go ahead and implement specific property formatters for them. So rather than having one class with tons of overloads for all the specific cases, you have dedicated classes that handled the formatting logic. In this example there is a DateTimePropertyFormatter
and a BooleanPropertyFormatter
and they would be implemented like so:
public class DateTimePropertyFormatter : IPropertyFormatter<DateTime>
{
public string FormatValue(DateTime value)
{
//DateTime customised formatting logic
return "<b>" + value.ToString("yyyyMMdd") + "</b>";
}
}
public class BoolPropertyFormatter : IPropertyFormatter<bool>
{
public string FormatValue(bool value)
{
//bool customised formatting logic
if (value)
return "yeaaah";
else
return "nope";
}
}
You could have many more classes such as List and so on, each with their own formatting logic keeping inline with the single responsibility principle
Right, so we have our formatters, how do we get all our formatters to run? This is where the FormatterResolver
comes into play. You can register formatters and they will
/// <summary>
/// Class responsible for getting the right format resolver for a given type
/// </summary>
public class FormatterResolver
{
private ObjectPropertyFormatter _objectPropertyFormatter;
private Dictionary<Type, object> _registeredFormatters;
public FormatterResolver()
{
_registeredFormatters = new Dictionary<Type, object>();
_objectPropertyFormatter = new ObjectPropertyFormatter();
}
public void RegisterFormatter<T>(IPropertyFormatter<T> formatter)
{
_registeredFormatters.Add(typeof(T), formatter);
}
public Func<string> GetFormatterFunc<T>(T value)
{
object formatter;
if (_registeredFormatters.TryGetValue(typeof(T), out formatter))
{
return () => (formatter as IPropertyFormatter<T>).FormatValue(value);
}
else
return () => ( _objectPropertyFormatter.FormatValue(value));
}
}
You would need somewhere to store an instance of a formatResolver and register all the formatters.
public FormatterResolver _formatResolver;
public void RegisterFormatResolvers()
{
_formatResolver = new FormatterResolver();
_formatResolver.RegisterFormatter(new BoolPropertyFormatter());
_formatResolver.RegisterFormatter(new DateTimePropertyFormatter());
//...etc
}
Your method would then look something like this:
public string GetFormattedAttribute<T>(T propertyValue)
{
return _formatResolver.GetFormatterFunc(propertyValue)();
}
So, time to put it to the test, does this all work? This is a quick sanity test that shows that the above code works as expected.
[TestMethod]
public void TestFormatResolvers()
{
RegisterFormatResolvers();
Assert.AreEqual("yeaaah", GetFormattedAttribute(true));
Assert.AreEqual("nope", GetFormattedAttribute(false));
Assert.AreEqual("<b>20120120</b>", GetFormattedAttribute(new DateTime(2012, 01, 20)));
Assert.AreEqual("5", GetFormattedAttribute(5));
}
If your formatting logic is also dependent on propertyName then all you would need to do is amend the interface to:
public interface IPropertyFormatter<T> {
string FormatValue(string propertyName, T value);
}
and implement the descendent classes accordingly
Upvotes: 1
Reputation: 174477
This has nothing to do with an override. You are creating an overload of GetFormattedAttribute
. If you call GetFormattedAttribute
on a variable of type Base
it doesn't know about the overload in the derived class, even if the instance in fact is of the derived type.
To put it simply: Your overload in the derived class will only be called, of it is being called on a variable of the derived class or a class that derives from it. Because the method is protected, I assume that you call GetFormattedAttribute
somewhere in your base class. In that scenario, it is impossible for you to use the overloaded GetFormattedAttribute
in the derived class.
One way to achieve the result you are looking for would be something like this:
object
parameter in your derived classList<Contact>
call that overload, otherwise call the base implementationSomething like this:
class Derived : Base
{
protected override string GetFormattedAttribute(string propertyName,
object propertyValue)
{
var list = propertyValue as List<Contact>;
if(list == null)
return base.GetFormattedAttribute(propertyName, propertyValue);
else
return GetFormattedAttribute(propertyName, list);
}
protected string GetFormattedAttribute(string propertyName,
List<Contact> contacts)
{
// ...
}
}
Upvotes: 3