Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Accessing Static Methods on a Generic class in c#

I have the following situation in code, which I suspect may be a bit dodgey:

I have a class:

abstract class DataAccessBase<T> : IDataAccess where T : AnotherAbstractClass

This class DataAccessBase also has a static factory method which creates instances of derived classes of itself using an enum value in a which statement to decide which derived type to create:

static IDataAccess CreateInstance(TypeToCreateEnum)

Now, the types derived from DataAccessBase<T> are themselves NOT generic, they specify a type for T:

class PoLcZoneData : DataAccessBase<PoLcZone> // PoLcZone is derived from AnotherAbstractClass

So far I am not sure if this is pushing the limits of good use of generics, but what I am really concerned about is how to access the static CreateInstance() method in the first place:

The way I am doing this at the moment is to simply pass any type T where T : AnotherAbstractClass. In particular I am passing AnotherAbstractClass itself. This allows compilation just fine, but it does seem to me that passing any type to a generic class just to get at the statics is a bit dodgey.

I have actually simplified the situation somewhat as DataAccessBase<T> is the lower level in the inheritance chain, but the static factory methods exists in a middle tier with classes such as PoLcZoneData being the most derived on the only level that is not generic.

What are peoples thoughts on this arrangement?

like image 238
MrLane Avatar asked Sep 01 '09 03:09

MrLane


2 Answers

You are allowed to have a non-generic class of the same name... perhaps something like:

abstract class DataAccessBase<T> : IDataAccess where T : AnotherAbstractClass
{
    ...
}
static class DataAccessBase
{
    public static IDataAccess CreateInstance(TypeToCreateEnum) {...}
}

Now you can use DataAccessBase.CreateInstance without any redundant T. Typically, you might call internal methods on DataAccessBase<T> from DataAccessBase - although I suspect in your scenario you might also need a little reflection / MakeGenericType.

like image 147
Marc Gravell Avatar answered Sep 20 '22 05:09

Marc Gravell


I've ran into a similar problem a while ago ("how to overload static methods") and I solved it with Reflection.

Here's my situation :

1) public abstract class AuditObject<T> : ActiveRecordBase<T> (yes I'm using ActiveRecord) and

2) public class Employee : AuditObject<Employee>

In both of them I define some static Methods, e.g.

public static DataTable GetLookupTable(String where, Int32 topRows)
{
    return doExtremelyCleverStuffToFetchData(where, topRows);
}

(in #2 you need public **new** static or else you get a compiler warning)

As the code is, when I call e.g.

DataTable myList = AuditObject<T>.GetLookupTable("inactive = 0", 100);

...and T is Employee, the static method is not "overriden" i.e. the one that is executed is the method in (1), not (2).

So in (1) I modified the static methods (in this example, GetLookupTable) like this :

public static DataTable GetLookupTable(String where, Int32 topRows)
{
    DataTable tbl = null;

    Boolean hasOverride = hasMethodOverride("GetLookupTable");

    if (hasOverride)
    {
        tbl = invokeStaticMethod<T>(
            "GetLookupTable", new Object[2] { where, topRows })
            as DataTable;
    }
    else
    {
        tbl = doExtremelyCleverStuffToFetchData(where, topRows);
    }

    return tbl;
}

Here's how I find out if the static method exists :

private static Boolean hasMethodOverride(String methodName)
{
    var methodQuery =
        from method in typeof(T).GetMethods(
            BindingFlags.Static | BindingFlags.Public | BindingFlags.InvokeMethod)
        where method.Name == methodName
        select method;

    return methodQuery.Count() > 0;
}

And here's how the "override" method is called :

public static Object invokeStaticMethod<T>(String MethodName, Object[] Args)
{
    return typeof(T).InvokeMember(MethodName,
        BindingFlags.Public | BindingFlags.Static | BindingFlags.InvokeMethod,
        null, null, Args);
}

Voila ! When I call DataTable myList = AuditObject<T>.GetLookupTable("inactive = 0", 100); and T is Employee, I get results from the static method defined in the Employee class.

Hope this helps,

Dimitris

like image 35
Jim Andrakakis Avatar answered Sep 22 '22 05:09

Jim Andrakakis