开发者

Given a constrained generic method can I call a non-generic method passing the actual type of the generic parameter

Awkward title I know, this is best explained in code. Given a set of classes:

public abstract class MyB开发者_如何学编程aseType
{
    public string Message { get; set; }
}

public class MySuperType : MyBaseType
{
    public string AdditionalInfo { get; set; }
}

public class MyOtherSuperType : MyBaseType
{
    public DateTime Started { get; set; }
    public DateTime Finished { get; set; }
}

Is there a way to write a generic method that calls a non-generic method passing the generic type whilst interpreting the passed type as its actual type and not the base type. That is, I want to write something like this:

public void OutputFormattedTypeInfo<T>(T theType) where T : MyBaseType
{
    OutputFormattedTypeInfo(theType as T);
}

public void OutputFormattedTypeInfo(MySuperType theType)
{
    System.Console.WriteLine(String.Format("{0} and {1}", theType.Message, theType.AdditionalInfo));
}

public void OutputFormattedTypeInfo(MyOtherSuperType theType)
{
    System.Console.WriteLine(String.Format("{0} - Start: {1}, End: {2}", theType.Message, theType.Started, theType.Finished));
}

But obviously theType as T is interpreted as the base type. I know I can use reflection like this:

Type type = typeof(MyBaseTypeDisplayFormatter);
    MethodInfo method = type.GetMethod(
                                    "FormatSpecific",
                                    BindingFlags.Instance | BindingFlags.NonPublic,
                                    null,
                                    new[] { update.GetType() },
                                    null);

    return (MyBaseTypeDataItem)method.Invoke(this, new object[] { update });

but it just feels inelegant. Is there a better way?


As Aliostad said, what your trying to do is no longer generic and it would be better to just use overloads. It looks like you are trying to do something similar to template specialization in C++, where depending on the generic type it calls different methods.

Here is an example where I implemented a sort of generic specialization using reflection, you might be able to apply a similar pattern if overloading method wont work for you. If you can cache the results of reflection and only call GetMethod once then it turns out to be not too slow. Inside a class generic by T there is a method that calls:

if (_serializeDataToStream == null)
    _serializeDataToStream = (Action<BinaryWriter, int, T[]>)GetTypeSpecificSerializationMethod();

_serializeDataToStream(writer, _size, _data);

Where the GetTypeSpecific method uses reflection to create a delegate

/// <summary>
/// Returns a delegate that points at the static type specific serialization method
/// </summary>
/// <returns></returns>
private Delegate GetTypeSpecificDeserializationMethod()
{
    if (typeof(T) == typeof(double))
    {
        MethodInfo method = this.GetType().GetMethod("DeserializeDouble", BindingFlags.Static | BindingFlags.NonPublic);
        return Delegate.CreateDelegate(typeof(Action<BinaryReader, int, T[]>), method);
    }
    else if (typeof(T) == typeof(ushort))
    {
        MethodInfo method = this.GetType().GetMethod("DeserializeUshort", BindingFlags.Static | BindingFlags.NonPublic);
        return Delegate.CreateDelegate(typeof(Action<BinaryReader, int, T[]>), method);
    }
    else if (typeof(T) == typeof(DateTime))
    {
        MethodInfo method = this.GetType().GetMethod("DeserializeDateTime", BindingFlags.Static | BindingFlags.NonPublic);
        return Delegate.CreateDelegate(typeof(Action<BinaryReader, int, T[]>), method);
    }
    else if (typeof(T) == typeof(bool))
    {
        MethodInfo method = this.GetType().GetMethod("DeserializeBool", BindingFlags.Static | BindingFlags.NonPublic);
        return Delegate.CreateDelegate(typeof(Action<BinaryReader, int, T[]>), method);
    }

    throw new NotImplementedException("No deserialization method has been setup for type " + typeof(T).FullName);
}

/// <summary>
/// Serialize double[] to BinaryWriter
/// </summary>
/// <param name="writer"></param>
/// <param name="size"></param>
/// <param name="data"></param>
private static void SerializeDouble(BinaryWriter writer, int size, double[] data)
{
    for (int i = 0; i < size; i++)
    {
        writer.Write(data[i]);
    }
}


The problem here is you are not really expressing a generality. You have to implement OutputFormattedTypeInfo for each and every base type, so you might as well forget about the generic method and simply use the overloads (you do not need generics here):

public void OutputFormattedTypeInfo(BaseType theType)
{
    // ...
}

public void OutputFormattedTypeInfo(MySuperType theType)
{
    // ...
}

public void OutputFormattedTypeInfo(MyOtherSuperType theType)
{
    // ...
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜