V.B.
V.B.

Reputation: 6382

How to use underlying generic types of generics C#

I have an internal generic method byte[] catchAllOperation<T>(T genericItem) and another one byte[] arrayOperation<T>(T[] genericArray). Additionally, I have a highly specialized operation byte[] specializedOperation<TKey,TValue>(CustomMap<Tkey,TValue> customMap).

How to write a method like this? (pseudocode):

public byte[] universalOperation<T>(T anything){
   if(T is CustomMap<UKey,UValue>){      // UKey/UValue are unknown
       return specializedOperation<UKey,UValue>(anything);
   } else if(T is U[]){                    // U is unknown
       return arrayOperation<U>(anything);
   }else{
       return catchAllOperation<T>(anything);
   }
}

How to get U and how to call arrayOperation<U>(anything); with U if I have only T (and the same for CustomMap<>)? Most questions I have seen assume that U is already known. Serializers use reflection to construct a separate method for each concrete type and then cache the method. But here I only want to redirect/dispatch a universal method to special cases when I could detect these cases.

I will have more types similar to CustomMap, so there is time to make any radical changes to code/approach. All special methods leverage the fact that for blittable underlying types some transformations and custom layout significantly boost the compression ratio of custom types. For custom types I could implement an interface, but for generic arrays it is not an option.

Upvotes: 1

Views: 110

Answers (2)

user2160375
user2160375

Reputation:

Like you are able to read comments it is not possible with generics. The only type is T do getting U and other types is not possible without reflection. Sample solution:

public static void UniversalOperation<T>(T anything)
{
    // check for null etc.
    var anythingType = anything.GetType();

    if (anythingType.IsGenericType &&
          anythingType.GetGenericTypeDefinition() == typeof(CustomMap<,>))
    {
        var genericArgs = anythingType.GetGenericArguments();
        var keyType = genericArgs[0];
        var valueType = genericArgs[1];

        return specializedOperation(keyValue, valueType, anything);
    }
    else if (anythingType.IsArray)
    {
        var elemType = anythingType.GetElementType();
        return arrayOperation(elemType, anything);
    }
    else
    {
        //T is parameter, so you can pass it
        catchAllOperation<T>(anything);
    }
}

Unfortunately, solution above required non-generic version of specializedOperation. Anyway, most of serializes (did I understand corretly, you serialize it?) share with non-generic overloads.

Upvotes: 1

Jon Skeet
Jon Skeet

Reputation: 1500785

Your question isn't entirely clear to me, but it sounds like you basically need to look in typeof(T) with reflection. Fortunately, dynamic typing in C# means you can get the compiler team to do the hard work - admittedly at execution time:

// Note: all names changed to be more conventional
public byte[] UniversalOperation<T>(T value)
{
    dynamic d = value;
    return DynamicOperation(d);
}

private byte[] DynamicOperation<UKey, UValue>(CustomMap<UKey, UValue> map)
{
    // Do stuff with the map here
}

private byte[] DynamicOperation<U>(U[] array)
{
    // Do something with the array here
}

private byte[] DynamicOperation(object value)
{
    // Fallback
}

Note that your UniversalOperation method doesn't have to be generic now - it will just use the execution-time type of the value. Of course, that means it may not be quite as you originally intended - for example, if the value is null, you're in trouble - whereas you could have potentially worked with typeof(T) despite that. Without knowing what you're trying to achieve, we can't tell whether or not that's a problem.

Upvotes: 5

Related Questions