jeudi 4 juin 2015

Extension methods with base and sub-classes

I am having trouble writing extension methods with non-abstract base classes and sub-classes that select the appropriate extension method.

Example Code

I have a very simple example below (abstracted from a much bigger project) that uses the extension method "Run". The expected output is listed in the comment next to each class.

public class Parent { }; // Should output "Parent"
public class ChildA : Parent { }; // Should output "Child A"
public class ChildB : Parent { }; // Should output "Parent"

// Expected Output: ChildA, Parent, Parent
public class Program
{
    public static void Main()
    {
        var commands = new List<Parent>() { new ChildA(), new ChildB(), new Parent() };
        Console.WriteLine(string.Join(", ", commands.Select(c => c.Run())));
    }
}

Here are my attempts so far, but there has to be a cleaner way to do this:

  1. No type-checking - Results in the Parent extension method being used exclusively (Parent, Parent, Parent)
  2. Explicit type checking - Right output, but have to explicitly check types for each extension possibility (ChildA, Parent, Parent)
  3. Attempted Convert.ChangeType to dynamic type - Run-time exception since extension won't catch the dynamic type (no output)
  4. Attempted generic cast with reflection - not quite operational yet, but not sure if approach is even valid

List of attempts

public static class Extensions
{
    public static string Run(this ChildA model)
    {
        return "ChildA";
    }
    public static string Run(this Parent model)
    {
        return model.Run1(); // Change to test different approaches
    }
    public static string Run1(this Parent model) // No type-checking
    {
        return "Parent";
    }
    public static string Run2(this Parent model) // Explicitly check sub-types
    {
        if (model is ChildA)
            return ((ChildA)model).Run();
        else
            return "Parent";
    }
    public static string Run3(this Parent model) // Attempted dynamic type conversion
    {
        if (model.GetType().BaseType == typeof(Parent))
        {
            dynamic changedObj = Convert.ChangeType(model, model.GetType());
            return changedObj.Run();
        }
        else
            return "Parent";
    }
    public static string Run4(this Parent model) // Attempted reflected generic type conversion
    {
        if (model.GetType().BaseType == typeof(Parent))
        {
            var method = typeof(Extensions).GetMethod("Cast");
            var generic = method.MakeGenericMethod(new[] { model.GetType() });
            //var generic = generic.Invoke(new object(), null);
            //return generic.Run();
            return "Not working yet";
        }
        else
            return "Parent";
    }
    public static T Cast<T>(this object input)
    {
        return (T) input;   
    }

}





Aucun commentaire:

Enregistrer un commentaire