views:

99

answers:

4

I am trying to provide an interface to an abstract generic base class. I want to have a method exposed on the interface that consumes the generic type, but whose implementation is ultimately handled by the classes that inherit from my abstract generic base.

However I don't want the subclasses to have to downcast to work with the generic type (as they already know what the type should be).

Here is a simple version of the only way I can see to get it to work at the moment.

public interface IFoo
{
    void Process(Bar_base bar);
}

public abstract class FooBase<T> : IFoo 
    where T : Bar_base
{
    abstract void Process(T bar);

    // Explicit IFoo Implementation
    void IFoo.Process(Bar_base bar)
    {
        if (bar == null) throw new ArgumentNullException();

        // Downcast here in base class (less for subclasses to worry about)
        T downcasted_bar = bar as T;
        if (downcasted_bar == null)
        {
            throw new InvalidOperationException(
                          string.Format("Expected type '{0}', not type '{1}'",
                                        T.ToString(), bar.GetType().ToString());
        }

        //Process downcasted object.
        Process(downcasted_bar);            
    }

}

Then subclasses of FooBase would look like this...

public class Foo_impl1 : FooBase<Bar_impl1>
{
     void override Process(Bar_impl1 bar)
     {
         //No need to downcast here!
     } 
}

Obviously this won't provide me compile time Type Checking, but I think it will get the job done...

Questions:
1. Will this function as I think it will?
2. Is this the best way to do this?
3. What are the issues with doing it this way?
4. Can you suggest a different approach?

Thanks!


Edit: In Response to many answers, It is a requirement that IFoo is not Generic. I need to be able to manipulate a collection of IFoo objects regardless of the generic Types they use.


Edit: In an effort to clarify the reasoning for this...

Bar_base contains a reference to type IFoo. And must call the process method to validate the data it contains. Think of IFoo as an object that contains validation logic for the Bar_base derived objects. When the validity of Bar_base object is questioned, it calls Process on its IFoo reference to validate itself.

The reason IFoo can't be generic is that I need to be able to reference a collection of IFoo independent of the Bar_base classes.

I am going to try the approach of having two interfaces a Generic one that contains the Process method, and non-generic one that doesn't.

+1  A: 

In the case where IFoo cannot be generic, it's very typical in a case like this to have two interfaces, IFoo<T> and IFoo, where IFoo uses the most base class supported. Think IEnumerable<T> and IEnumerable. The non-generic version is usually hidden as an interface overload, so it only comes into play when accessing the class via the non-generic interface.

Dan Bryant
+1 going to try out the two interface approach and see if it will work for me.
Nate Heinrich
A: 

You could just make IFoo generic:

public interface IFoo<T> where T : Bar_base
{
    void Process(T bar);
}
Lee
A: 

Any reason you don't just use a generic interface:-

public interface IFoo<T>
  where T:Bar_base
{
    void Process(T bar);
}
Hightechrider
+1  A: 

Given your constraint that T is of type Bar_base, and (in your example) only using T for Process(T bar), I'm not sure why you're using generics at all. If

abstract Process(Bar_base bar)

is all you need, then that should be sufficient for class overriding with

void override Process(Bar_impl1 bar)

and your abstract class Bar_base is a sufficient type constraint.

Unless I'm missing something....

Another useful trick is to provide both generic and non-generic interface definitions, where the non-generic provides non-generic access methods and properties, and the generic interface provides only the additional methods and/or properties that requires a generic type. Clients can specify either interface, depending on whether they're in a position to share the generic type.

interface IFoo
{
...
}

interface IFoo<T> : IFoo where T : Bar_base
{
...
}

But I'm not sure that this would satisfy your needs in this case.

Cylon Cat
+1 going to try out the two interface approach and see if it will work for me.
Nate Heinrich
Had to accept Dan's answer as he posted his first. But thanks for your help!
Nate Heinrich