tags:

views:

87

answers:

2

Sorry for the vague title, but I wasn't sure how to summarize this in one phrase. I have a situation with a lot of redundant C# code, and it really looks like some kind of crafty trick using some property of inheritance or generics would solve this. However, I'm not a terribly experienced programmer (particularly with C#) and just can't quite see the solution.

The situation, in simplified form, looks something like this. I have a bunch of classes that all inherit from one type.

public class Foo : SuperFoo
{
     ...
     public Foo SomeMethod() { ... }
}
public class Bar : SuperFoo
{
     ...
     public Bar SomeMethod() { ... }
}
public class Baz : SuperFoo
{
     ...
     public Baz SomeMethod() { ... }
}
...    
public class SuperFoo
{
     ...
}

The problem comes when collections of these objects need to be processed. My first-draft solution (the bad one) looks like this:

public void SomeEventHasHappened(...)
{
     ProcessFoos();
     ProcessBars();
     ProcessBazes();
     ...
}

public void ProcessFoos()
{
     ...
     foreach (var foo in fooList)
     {
          ...
          foo.SomeMethod();
     }
}
public void ProcessBars()
{
     ...
     foreach (var bar in barList)
     {
          ...
          bar.SomeMethod();
     }
}

...and so on. The problem is that basically all of the code in the ProcessX methods is the same, other than the type of the objects that are being operated on. It would be nice to consolidate all of these into one method for obvious reasons.

My first thought was to just make a generic Process() method that took a List<SuperFoo> as a parameter and just proceed from there. The problem is that a generic SuperFoo does not have a SomeMethod(), and it can't have one because each of the child classes' SomeMethod() has a different return type, so having overrides doesn't work.

+2  A: 

I usually add an interface which operates on base types.

interface ISuperFoo
{
    public ISuperFoo SomeMethod() { ... }
}

public class Foo : SuperFoo, ISuperFoo
{
     // concrete implementation
     public Foo SomeMethod() { ... }

     // method for generic use, call by base type
     public ISuperFoo ISuperFoo.SomeMethod() 
     { 
       return SomeMethod(); 
     }
}

public void Processs()
{
     ...
     foreach (var iSuperFoo in list)
     {
          ...
          iSuperFoo.SomeMethod();
     }
}

Of course it depends what you are using the result for.

Sometimes you can make it easier using generics, but you also can end up in a mess. Sometimes it is just easier to downcast somewhere. Of course, you try to avoid this whenever you can afford it.

Stefan Steinegger
This is pretty nice, and probably something I should have thought of! Thanks for the answer.
jloubert
+2  A: 

Here's an example of how this might work using generics and making SuperFoo an abstract class.

public interface ISuperFoo
{
    ...
}

public abstract class SuperFoo<T> where T : ISuperFoo
{
    public abstract T SomeMethod();
}

public class BazReturn : ISuperFoo
{
    ...
}

public class Baz: SuperFoo<BazReturn>
{
    public override BazReturn SomeMethod()
    {
        throw new NotImplementedException();
    }
}

public class BarReturn : ISuperFoo
{
    ...
}

public class Bar : SuperFoo<BarReturn>
{
    public override BarReturn SomeMethod()
    {
        throw new NotImplementedException();
    }
}

public static class EventHandler
{
    public static void SomeEventHasHappened(List<SuperFoo<ISuperFoo>> list)
    {
        foreach (SuperFoo<ISuperFoo> item in list)
        {
            ISuperFoo result = item.SomeMethod();
        }
    }
}

You could replace the ISuperFoo interface with a concrete class if needed, but then you would have to cast the return value which kind of defeats the purpose.

public abstract class SuperFoo<T>
{
    public abstract T SomeMethod();
}

public class Foo : SuperFoo<int>
{
    public override int SomeMethod()
    {
        throw new NotImplementedException();
    }
}

public static class EventHandler
{
    public static void SomeEventHasHappened(List<SuperFoo<int>> list)
    {
        foreach (SuperFoo<int> item in list)
        {
            item.SomeMethod();
        }
    }
}
sgriffinusa
Thanks for the response. But I'm a little confused by the FooReturn and Foo classes in the first part of your answer. If I understand this correctly, it looks like an instance of Foo's SomeMethod() will return a FooReturn object instead of a Foo object. So therefore, all the other code that used to be in Foo should now be in FooReturn, while Foo only needs to contain the SomeMethod(). If this is indeed the case, wouldn't this break code in some other place which has `Foo fooObject = anotherFooObject.SomeMethod();`?
jloubert
SomeMethod's return type should always be treated as returning ISuperFoo. You'll notice that the abstract SuperFoo's generic varialbe T must extend ISuperFoo. This is what gives the consistent return type that is required. I've added a Bar example to my code that hopefully will clarify.
sgriffinusa
I also renamed the child class from Foo to Baz that will hopefully further clarify the difference between the interface, abstract, and the extending types.
sgriffinusa