如何使.Net泛型方法对值类型和引用类型的行为有所不同?

时间:2008-12-23 11:57:25

标签: c# generics

我有一个方法的两个实现,一个用于值类型,另一个用于引用类型:

public static Result<T> ImplRef(T arg) where T : class {...}
public static Result<T> ImplVal(T arg) where T : struct {...}

我想写一个方法来调用正确的实现,就像这个

public static Result<T> Generic(T arg) {
    if (typeOf(T).IsValueType)
         return ImplVal(arg);
    else
         return ImplRef(arg);
}

显然,上面的实现没有编译。如何以最少的反射来做到这一点?

4 个答案:

答案 0 :(得分:4)

泛型的想法通常是用你给出的任何输入做同样的逻辑,虽然显然你需要实用。就个人而言,我可能会使用两种不同的方法,而不是将它们暴力强加到同一种方法中,但这样就很难通过只知道T的通用方法进行调用。虽然: class方法可能有效,但无法满足静态代码中的: struct / MakeGenericMethod,但速度会慢一些。

    // slow; use with caution
    public static Result<T> Generic<T>(T arg) {
        if (typeof(T).IsValueType)
            return (Result<T>)typeof(Program).GetMethod("ImplVal")
                .MakeGenericMethod(typeof(T))
                .Invoke(null, new object[] {arg});
        else
            return (Result<T>)typeof(Program).GetMethod("ImplRef")
                .MakeGenericMethod(typeof(T))
                .Invoke(null, new object[] { arg });
    }

(用托管方法的类型替换typeof(Program)

备选方案(如Jon所说)是将(类型化)委托缓存到方法中:

public static Result<T> Generic<T>(T arg) {
    return Cache<T>.CachedDelegate(arg);
}

internal static class Cache<T>
{
    public static readonly Func<T, Result<T>> CachedDelegate;
    static Cache()
    {
        MethodInfo method;
        if (typeof(T).IsValueType)
            method = typeof(Program).GetMethod("ImplVal")
                .MakeGenericMethod(typeof(T));
        else
            method = typeof(Program).GetMethod("ImplRef")
                .MakeGenericMethod(typeof(T));
        CachedDelegate = (Func<T, Result<T>>)Delegate.CreateDelegate(
            typeof(Func<T, Result<T>>), method);
    }
}

更多工作,但会很快。静态构造函数(或者你可以使用property / null检查)确保我们只做一次艰苦的工作。

答案 1 :(得分:2)

您实际上使用 ImplRefImplVal中的约束吗?如果不是(即如果它只是这样你可以表现不同)你可以删除约束,使方法保密,并从Generic恰当地调用它们 - 或者可能有:

public static Result<T> ImplRef(T arg) where T : class 
{
    return ImplRefImpl(arg);
}

private static Result<T> ImplRefImpl(T arg)
{
    // Real code
}

public static Result<T> ImplVal(T arg) where T : struct
{
    return ImplValImpl(arg);
}

private static Result<T> ImplValImpl(T arg)
{
    // Real code
}

顺便说一下,如果T是可以为空的类型,您应该考虑该怎么做 - 它实际上并不满足classstruct约束。

顺便说一下,另一种测试T是值类型的方法(可能更有效 - 不确定)是:

if (default(T) == null)

请注意,与IsValueType的测试不同,这将为可空类型“传递”,因此行为不相同。

答案 2 :(得分:0)

是否有理由不能使用两个单独的实现(可能都使用第三个常见实现)?

使用typeof表示您正在执行RTTI,这会让您的应用变得更慢。使用编译器(通过使用类和结构的单独实现)会更快,因为所有关于类型的决定都将由编译器在JIT编译期间处理。

答案 3 :(得分:-1)

为什么不让编译器选择?

public static Result<T> ToResult<T>(this T arg) where T: class
{ return new ImplRef(arg); }

public static Result<T> ToResult<T>(this T arg) where T: struct
{ return new ImplVal(arg)

用作:

"hi".ToResult();
3.ToResult();