Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Interface Base class instantiation via generic method

I have an interface that for example's sake looks like this:

interface IFoo<TEnum> where TEnum : struct, IConvertible, IComparable, IFormattable
{
  TEnum MyEnum { get; set; }
}

I then have an abstract base class that looks like this:

abstract class FooBase<TEnum> : IFoo<TEnum> where TEnum : struct, IConvertible, IFormattable, IComparable
{
  public TEnum MyEnum { get; set; }
}

I then inherit from the base class like so:

class MyFoo : FooBase<MyFoo.MyFooEnum>
{
  public enum MyFooEnum
  {
    Foo1,
    Foo2,
  }
}

How can I instantiate a MyFoo from a generic method with type parameter FooBase?

I'm pretty much looking for something like this:

static class FooMethods
{
  public static TFooClass GetFoo<TFooClass>() where TFooClass : FooBase, new()
  {
    TFooClass fooclass = new TFooClass();
    return fooclass;
  }
}

The problem I have is that it wants type parameters for FooBase, but in reality I don't really care what the parameters are since my MyFoo has those type parameters already.

like image 914
ldam Avatar asked Oct 22 '15 15:10

ldam


People also ask

Can a generic implement an interface?

Only generic classes can implement generic interfaces. Normal classes can't implement generic interfaces.

How do you declare a generic interface?

You can declare variant generic interfaces by using the in and out keywords for generic type parameters. ref , in , and out parameters in C# cannot be variant. Value types also do not support variance. You can declare a generic type parameter covariant by using the out keyword.

What is a generic interface?

A generic interface is primarily a normal interface like any other. It can be used to declare a variable but assigned the appropriate class. It can be returned from a method. It can be passed as argument. You pass a generic interface primarily the same way you would an interface.


1 Answers

You can inherit your generic class from a common base:

abstract class FooBase {
}

abstract class FooBase<TEnum> : FooBase, IFoo<TEnum>
where TEnum : struct, IConvertible, IFormattable, IComparable {
   public TEnum MyEnum { get; set; }
}

public static TFooClass GetFoo<TFooClass>()
where TFooClass : FooBase, new() {
   TFooClass fooclass = new TFooClass();
   return fooclass;
}

But you won't be able to access the MyEnum property with a generic constraint of FooBase. (And how could you, without having specified the type?)

That, or you need to add another type parameter to GetFoo:

abstract class FooBase<TEnum> : IFoo<TEnum>
where TEnum : struct, IConvertible, IFormattable, IComparable {
   public TEnum MyEnum { get; set; }
}

public static TFooClass GetFoo<TFooClass, TEnum>()
where TFooClass : FooBase<TEnum>, new()
where TEnum : struct, IConvertible, IFormattable, IComparable {
   TFooClass fooclass = new TFooClass();
   return fooclass;
}

UPDATE: Another thing I could point out is that if you find that you need to call this GetFoo method a lot, then if you put it in an instance class instead of a static class, you can push one or both of the type arguments up into the class instead of always specifying it in the method. This can make some code a little less verbose, but really only if you call this method a lot. Ex:

public sealed FooFactory<TEnum>
where TEnum : struct, IConvertible, IFormattable, IComparable {
   public static TFooClass GetFoo<TFooClass>()
   where TFooClass : FooBase<TEnum>, new() {
      TFooClass fooclass = new TFooClass();
      return fooclass;
   }
}

...

var factory = new FooFactory<SomeEnum>();
var foo1 = factory.GetFoo<SomeFooClass1>();
var foo2 = factory.GetFoo<SomeFooClass2>();

// or the other way:

var factory = new FooFactory<SomeFooClass>();
var foo1 = factory.GetFoo<SomeEnum1>();
var foo2 = factory.GetFoo<SomeEnum2>();
like image 129
Dave Cousineau Avatar answered Sep 25 '22 04:09

Dave Cousineau