Instantiate a class which implements a generic interface

981 views Asked by At

I have an interface for specifying GetData method which returns one instance of the class by its ID

public interface ILabelData<T> {
  T GetData(object id);
}

and also have many different classes those who implements the interface and having members of course:

public class BTAC : ILabelData<BTAC> {
  // members...
  // and interface impl:
  public BTAC GetData(object id) {
    return null;
  }
}
public class KTAC : ILabelData<KTAC> {
  // members...
  // and interface impl:
  public KTAC GetData(object id) {
    return null;
  }
}

Within a calling method I would like to instantiate a BTAC/KTAC/... class and call their GetData method. (The purpose of it is becasue after I have the required instance I want to get its members and attributes. Getting the members and attributes is not part of my question.)

ILabelData<object> o = Activator.CreateInstance(type, new object[] { myID });
^^^^^^^^^^^^^^^^^^^^
object data = o.GetData(myID);

the problem is compiler error Cannot implicitly convert type 'object' to 'ILabelData< object >'

After instantiating the proper class of course I do need for the members and attributes, too, so it isn't enough to getting back ILabelData typed object.

How can I get a such kind of object? Some factory or whatever?

2

There are 2 answers

1
Luaan On BEST ANSWER
  1. You're mixing up the factory and the entity. That's unnecessary - it really doesn't make sense to have GetData be an instance method on BTAC.
  2. Activator.CreateInstance returns object - you need to explicitly cast it to the type you want.
  3. Neither of your types implements ILabelData<object>.

The thing is, with the code as is, there's no point in having ILabelData generic at all! If you're going to work with object anyway, remove the generic parameter from ILabelData and just do this:

var o = (ILabelData)Activator.CreateInstance(type, new object[] { myID });

var data = o.GetData(myID);

Having the interface (and GetData method) generic only makes sense if you actually use it that way.

If you usually use it directly, and you only need it like this in a few special cases (why?) you could also make the interface covariant:

public interface ILabelData<out T>
{
  T GetData(object id);
}

This will allow you to implement ILabelData<BTAC> in BTAC, giving you the "user friendly" BTAC GetData(object) method, while still allowing you to do the explicit cast to ILabelData<object>. Even then, I'd avoid this approach. It smells.

0
PrinceT On

Working Code here.

var instances = (from t in System.Reflection.Assembly.GetCallingAssembly().GetTypes()
                        where t.GetInterfaces().Any(x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof(ILabelData<>))
                                 && t.GetConstructor(Type.EmptyTypes) != null
                        select Activator.CreateInstance(t)).ToList();

        foreach (dynamic item in instances)
        {
            var res = item.GetData(1);
        }