Kevin P. Rice
Kevin P. Rice

Reputation: 5733

Call non-generic Method(Generic<Base>[] args) and pass array of Generic<Derived>?

Solution to call non-generic method and pass generic arguments with different generic types?

My imaginary dream:

void FooBulous(Foo<object>[] fooArray) {  } // Accept any 'Foo<BaseType>'

var fooArray = new Foo<object>[]    // Array of 'Foo<BaseType>'
{
  new Foo<Cat>(),
  new Foo<Dog>(),
};

FooBulous(fooArray); // Pass the 'Foo<BaseType>[]' array

My reality:

void BarBaric(object[] barArray) {  } // Can't constrain to 'Foo<>'

var barArray = new object[]  // Same problem
{
  new Bar<Cat>(),
  new Bar<Dog>(),
};

BarBaric(barArray); // Barbaric! I thought the 'void *ptr' days were over!

In summary:

void Fee(object[] params)      { /* WORKS! But not constrained to 'Foo<Base>' */ }
void Fie(Foo<Cat>[] params)    { /* Does not accept 'Foo<Dog>' */ }
void Foe(Foo<>[] params)       { /* ERROR: 'Type expected' */ }
void Fum(Foo<object>[] params) { /* Cannot convert 'Foo<Cat>' to 'Foo<object>' */ }

Clearly, this can't be done... Is there a clean alternative?

Upvotes: 0

Views: 212

Answers (1)

Jon Skeet
Jon Skeet

Reputation: 1500225

The problem is that a Foo<Cat> isn't a Foo<object>. Suppose Foo looked like this:

public class Foo<T>
{
    public void Method(T input)
    {
        ...
    }
}

Then a Foo<Cat> would always expect a Cat value for the input parameter to Method. But if you could treat Foo<Cat> as a Foo<object> you could do:

Foo<Cat> catFoo = new Foo<Cat>();
Foo<object> objectFoo = catFoo;
objectFoo.Method(new object()); // Eek! Type safety is broken!

Now generic variance is available in .NET 4 (and C# 4) but only for interfaces and delegates, and only for those decorated appropriate with out and in at the point of the type parameter declaration. That may or may not be useful to you.

Another option is to make Foo<T> derive from an abstract non-generic base class Foo which provides all the members which have nothing to do with T. Then you could write:

void Foobulous(Foo[] fooArray)

...

Foobulous(new Foo[] {
    new Foo<object>(),
    new Foo<Cat>(),
    new Foo<Dog>()
});

and all would be well, so long as Foobulous didn't need to use any of the methods which relied on T - which it shouldn't, given that it can take Foo values with different type parameters anyway.

Upvotes: 5

Related Questions