强制T作为代码中的引用类型[CS0452错误]

时间:2016-03-14 08:50:06

标签: c# generics interface reference-type

我对T有以下问题,并且通用方法的选项将其限制为例如引用类型(其中T:class)。所以我得到了以下代码:

public void mySave<T>(string filename, T obj)
{
    Session s = connect(filename);
    s.Save(obj);
}

public Document Save<TEntity>(TEntity entity) where TEntity : class;

无法声明mySave where T : class,因为我是从界面获取的。无论如何,我需要解决CS0452 (The type 'type name' must be a reference type in order to use it as parameter 'parameter name' in the generic type or method 'identifier of generic')

关于这样做的任何想法?

2 个答案:

答案 0 :(得分:2)

如果某个泛型参数有一些约束,那么用作参数的任何其他泛型参数必须至少提供相同的类型保证。

由于泛型是编译时的特性,可以想象Save<TEntity>需要TEntity作为引用类型,并且您可以给出一个可能是也可能不是值类型的参数。这会破坏仿制药的目的。

最好的办法是将整个通用约束(即T : class)添加到界面中。

答案 1 :(得分:1)

Sriram建议两种方式......更正确的方式(由MatíasFidemraizer探索)是改变界面。另一种方式是通过反思。我完全支持MatíasFidemraizer探索的方式,并认为这是“正确的”方式。但有时你不能做正确的事情,你必须做有效的事情。

这是通过反思的另一种方式。请注意,此代码超出了必要的范围,缓存了委托,以便使用相同T类型的其他调用不需要重用反射,并且会更快。

public static class SessionHelper
{
    public static void Save2<T>(this Session session, T obj)
    {
        SessionHelperImpl<T>.Save(session, obj);
    }

    private static class SessionHelperImpl<T>
    {
        public static readonly Action<Session, T> Save;

        static SessionHelperImpl() 
        {
            MethodInfo saveT = (from x in typeof(Session).GetMethods()
                                where x.Name == "Save" && x.IsGenericMethod
                                let genericArguments = x.GetGenericArguments()
                                where genericArguments.Length == 1
                                let parameters = x.GetParameters()
                                where parameters.Length == 1 && parameters[0].ParameterType == genericArguments[0]
                                select x).Single();

            MethodInfo save = saveT.MakeGenericMethod(typeof(T));

            Save = (Action<Session, T>)save.CreateDelegate(typeof(Action<Session, T>));
        }
    }
}

使用它像:

s.Save2(obj);

请注意,如果您尝试将非引荐类型作为obj传递,则会获得TypeInitializationException。你无法真正绕过where T : class的{​​{1}}。您只能在编译期间绕过它并将其延迟到运行时。