在通用参数中取消装箱可以为空的枚举会导致问题

时间:2015-07-29 14:41:41

标签: c# generics enums delegates nullable

我无法通过通用变量传递可以为空的枚举。

我目前正在使用反射为泛型类创建属性设置器。 这是代码

public static class program
{
    static void Main(string[] args)
    {
        someClass testclass = new someClass();
        PropertyInfo enumProperty = typeof(someClass).GetProperties()[0];
        Action<someClass, int> enumDelegate = CreateSetterFromPropertyInfo<someClass, int>(enumProperty);
        enumDelegate(testclass, 2);
        Console.WriteLine(testclass.enumVal.ToString()); // writes B

        object nullableSetter = null;
        PropertyInfo enumPropertyNullable = typeof(someClass).GetProperties()[1];

        if (enumPropertyNullable.PropertyType.GetGenericTypeDefinition() == typeof(Nullable<>))
        {
            if (enumPropertyNullable.PropertyType.GetGenericArguments().First().IsEnum)
            {
                // the step below determines the method signature. I cannot deviate from what the property tells me.
                // if I use int? instead of the PropertyType I get an ArgumentException.
                MethodInfo method = typeof(program).GetMethod("CreateSetterFromPropertyInfo", BindingFlags.Static | BindingFlags.NonPublic).MakeGenericMethod(new Type[] { typeof(someClass), enumPropertyNullable.PropertyType });
                nullableSetter = method.Invoke(null, new object[] { enumPropertyNullable });
            }
        }
        if (nullableSetter != null)
        {
            Action<someClass, someEnum?> cast1 = (Action<someClass, someEnum?>) nullableSetter;
            Action<someClass, Nullable<someEnum>> cast2 = (Action<someClass, Nullable<someEnum>>)nullableSetter;
            // Action<someClass, Nullable<int>> cast3 = (Action<someClass, Nullable<int>>)nullableSetter;
            // Action<someClass, Enum> cast4 = (Action<someClass, Enum>)nullableSetter;

            // at this point I am trying to cast a string to a enum. That works. However the parameter should be a nullable enum.
            Type subtype = enumPropertyNullable.PropertyType.GetGenericArguments().First();
            Enum result = Enum.Parse(subtype, "B") as Enum;
            // here is where I am getting the error. I can't find a way to cast the string I have to a nullable enum. 
            //cast1(testclass, result);
        }

    }

    private static Action<C, P> CreateSetterFromPropertyInfo<C, P>(PropertyInfo property)
    {

        Action<C, P> setForAnyProperty = (Action<C, P>)Delegate.CreateDelegate(typeof(Action<C, P>), null, property.GetSetMethod());
        return setForAnyProperty;
    }

    public class someClass
    {
        public someEnum enumVal {get; set;}
        public someEnum? enumValNullable { get; set; }
    }

    public enum someEnum
    {
        A = 1,
        B = 2
    }

}

我只会遇到可以为空的枚举问题,而不是正常的枚举问题。如果您运行该示例,您可以看到使用带有整数作为签名的委托,代码运行正常。

问题当可空的枚举迫使我在委托制定者签名中使用枚举本身时。当我实际上尝试用值填充对象时,代码没有引用有问题的枚举,这是使用泛型的一点。我也不确定为什么我不能使用int?对于可以为空的枚举。欢迎任何见解!

此时我很想使用PropertyInfo.SetValue方法而不是委托。它似乎有效。

1 个答案:

答案 0 :(得分:1)

更新:下面的新NullableEnumCopier类不了解someClass或someEnum。

public static class program
{
    static void Main(string[] args)
    {
        someClass testclass = new someClass();
        NullableEnumCopier<someClass, someEnum>.Copy(testclass);
    }

    public static class NullableEnumCopier<TClass, TEnum> where TEnum : struct
    {
        public static void Copy(TClass item)
        {
            PropertyInfo enumProperty = typeof(TClass).GetProperties()[0];
            Action<TClass, int> enumDelegate = CreateSetterFromPropertyInfo<TClass, int>(enumProperty);
            enumDelegate(item, 2);
            //Console.WriteLine(item.enumVal.ToString()); // writes B

            object nullableSetter = null;
            PropertyInfo enumPropertyNullable = typeof(TClass).GetProperties()[1];

            if (enumPropertyNullable.PropertyType.GetGenericTypeDefinition() == typeof(Nullable<>))
            {
                if (enumPropertyNullable.PropertyType.GetGenericArguments().First().IsEnum)
                {
                    // the step below determines the method signature. I cannot deviate from what the property tells me.
                    // if I use int? instead of the PropertyType I get an ArgumentException.
                    MethodInfo method = typeof(program).GetMethod("CreateSetterFromPropertyInfo", BindingFlags.Static | BindingFlags.NonPublic).MakeGenericMethod(new Type[] { typeof(TClass), enumPropertyNullable.PropertyType });
                    nullableSetter = method.Invoke(null, new object[] { enumPropertyNullable });
                }
            }
            if (nullableSetter != null)
            {
                Action<TClass, TEnum?> cast1 = (Action<TClass, TEnum?>) nullableSetter;
                Action<TClass, Nullable<TEnum>> cast2 = (Action<TClass, Nullable<TEnum>>)nullableSetter;
                // Action<TClass, Nullable<int>> cast3 = (Action<TClass, Nullable<int>>)nullableSetter;
                // Action<TClass, Enum> cast4 = (Action<TClass, Enum>)nullableSetter;

                // at this point I am trying to cast a string to a enum. That works. However the parameter should be a nullable enum.
                Type subtype = enumPropertyNullable.PropertyType.GetGenericArguments().First();
                Enum result = Enum.Parse(subtype, "B") as Enum;
                // here is where I am getting the error. I can't find a way to cast the string I have to a nullable enum. 
                cast1(item, (TEnum?)(object)result);
            }
        }
    }

    private static Action<C, P> CreateSetterFromPropertyInfo<C, P>(PropertyInfo property)
    {

        Action<C, P> setForAnyProperty = (Action<C, P>)Delegate.CreateDelegate(typeof(Action<C, P>), null, property.GetSetMethod());
        return setForAnyProperty;
    }

    public class someClass
    {
        public someEnum enumVal {get; set;}
        public someEnum? enumValNullable { get; set; }
    }

    public enum someEnum
    {
        A = 1,
        B = 2
    }

}