C# 强制类型设置程序接受兼容的类型

C# 强制类型设置程序接受兼容的类型,c#,C#,假设有一节课 public class MyClass { public decimal Id { get; set; } } 我需要使用Id属性的setter方法动态地为Id分配小数、整数、字节等,如下所示: var setterMethod = typeof(MyClass).GetMethods(...)...First(); setterMethod.Invoke(myClassInstance, (int)1); PropertyInfo property = typeof(MyC

假设有一节课

public class MyClass { public decimal Id { get; set; } }
我需要使用Id属性的setter方法动态地为Id分配小数、整数、字节等,如下所示:

var setterMethod = typeof(MyClass).GetMethods(...)...First();
setterMethod.Invoke(myClassInstance, (int)1);
PropertyInfo property = typeof(MyClass).GetProperty(nameof(MyClass.Id));
var value = ExtendedChangeType((int)1, valueType);
property.SetValue(myClassInstance, value);
但这不起作用,因为int和decimal类型不匹配

同时,这也很有效:

decimal decZero = 0;
int intZero = 0;
byte byteZero = 0;
var sample1 = new MyClass{ Id = decZero };  
var sample2 = new MyClass{ Id = intZero };  
var sample3 = new MyClass{ Id = byteZero };  
这意味着C可以隐式转换数字类型


如何使用类型设置器将整数、小数、字节等动态分配给Id属性?

重写答案以处理数字类型之间的转换,以及它们的可空和/或可枚举变体

要处理内置数字类型之间的转换,该方法将是您的朋友。只需确保该值实现了IConvertible接口原语.NET类型,如int或double

在可枚举类型和数值类型之间进行强制转换时,应改为使用。给定的值应与可枚举的基础类型匹配,因此要将十进制转换为基于整数的int,需要进行额外的转换

如果需要进行字符串解析,则需要处理可枚举值。对于普通数字类型,只要不尝试从小数点格式的字符串中解析整数类型,Convert.ChangeType就足够了

最后,Convert.ChangeType不适用于可为null的类型,因此需要首先提取底层类型。就是为了这个

综合所有这些,我们可以构建一个扩展的ChangeType方法:

public static object ExtendedChangeType(object value, Type targetType)
{
    if (value == null)
        return null;

    targetType = Nullable.GetUnderlyingType(targetType) ?? targetType;
    if (targetType.IsEnum)
    {
        if (value is string)
        {
            return Enum.Parse(targetType, value as string);
        }
        else
        {
            value = Convert.ChangeType(value, Enum.GetUnderlyingType(targetType));
            return Enum.ToObject(targetType, value);
        }
    }
    else
    {
        return Convert.ChangeType(value, targetType);
    }
}
然后我们可以这样使用它:

var setterMethod = typeof(MyClass).GetMethods(...)...First();
setterMethod.Invoke(myClassInstance, (int)1);
PropertyInfo property = typeof(MyClass).GetProperty(nameof(MyClass.Id));
var value = ExtendedChangeType((int)1, valueType);
property.SetValue(myClassInstance, value);

重写答案以处理数字类型之间的转换,以及它们的可空和/或可枚举变体

要处理内置数字类型之间的转换,该方法将是您的朋友。只需确保该值实现了IConvertible接口原语.NET类型,如int或double

在可枚举类型和数值类型之间进行强制转换时,应改为使用。给定的值应与可枚举的基础类型匹配,因此要将十进制转换为基于整数的int,需要进行额外的转换

如果需要进行字符串解析,则需要处理可枚举值。对于普通数字类型,只要不尝试从小数点格式的字符串中解析整数类型,Convert.ChangeType就足够了

最后,Convert.ChangeType不适用于可为null的类型,因此需要首先提取底层类型。就是为了这个

综合所有这些,我们可以构建一个扩展的ChangeType方法:

public static object ExtendedChangeType(object value, Type targetType)
{
    if (value == null)
        return null;

    targetType = Nullable.GetUnderlyingType(targetType) ?? targetType;
    if (targetType.IsEnum)
    {
        if (value is string)
        {
            return Enum.Parse(targetType, value as string);
        }
        else
        {
            value = Convert.ChangeType(value, Enum.GetUnderlyingType(targetType));
            return Enum.ToObject(targetType, value);
        }
    }
    else
    {
        return Convert.ChangeType(value, targetType);
    }
}
然后我们可以这样使用它:

var setterMethod = typeof(MyClass).GetMethods(...)...First();
setterMethod.Invoke(myClassInstance, (int)1);
PropertyInfo property = typeof(MyClass).GetProperty(nameof(MyClass.Id));
var value = ExtendedChangeType((int)1, valueType);
property.SetValue(myClassInstance, value);
如何使用类型设置器将整数、小数、字节等动态分配给Id属性

您可以尝试以下方法:

var converted = Convert.ChangeType((int)10, property.PropertyType);
property.SetValue(sample2, converted);
在这里,它可以动态地将整数、小数和字节分配给十进制属性

using System;

public class Program
{
    public static void Main()
    {
        var sample1 = new MyClass{Id = (decimal)0};
        var sample2 = new MyClass{Id = (int)0};
        var sample3 = new MyClass{Id = (byte)0};

        var property = typeof (MyClass).GetProperty(nameof(MyClass.Id));

        property.SetValue(sample1, Convert.ChangeType((decimal)10, property.PropertyType));
        property.SetValue(sample2, Convert.ChangeType((int)10, property.PropertyType));
        property.SetValue(sample3, Convert.ChangeType((byte)10, property.PropertyType));
    }
}

public class MyClass
{
    public decimal Id { get; set; }
}
如何使用类型设置器将整数、小数、字节等动态分配给Id属性

您可以尝试以下方法:

var converted = Convert.ChangeType((int)10, property.PropertyType);
property.SetValue(sample2, converted);
在这里,它可以动态地将整数、小数和字节分配给十进制属性

using System;

public class Program
{
    public static void Main()
    {
        var sample1 = new MyClass{Id = (decimal)0};
        var sample2 = new MyClass{Id = (int)0};
        var sample3 = new MyClass{Id = (byte)0};

        var property = typeof (MyClass).GetProperty(nameof(MyClass.Id));

        property.SetValue(sample1, Convert.ChangeType((decimal)10, property.PropertyType));
        property.SetValue(sample2, Convert.ChangeType((int)10, property.PropertyType));
        property.SetValue(sample3, Convert.ChangeType((byte)10, property.PropertyType));
    }
}

public class MyClass
{
    public decimal Id { get; set; }
}

你不能,因为在你的例子中,不变性是相关的

operator=隐式转换为所需的类型MethodBase.Invoke,而不是其他类型

调试代码,在方法调用时放置断点。请确保方法本身具有以下定义- {Void set_IdSystem.Decimal}哪个是明显的对吗?它接受一个decimal类型的参数,而编译器可以为您发挥神奇的作用,允许您在运行时隐式调用它,但情况有所不同

IL说明,显示使用赋值运算符执行操作时发生的情况

using System; using System.Collections.Generic; namespace ConsoleApp1 { class Program { static void Main(string[] args) { var testIntance = new Test(); testIntance.Id = (int)5; } } public class Test { public decimal Id { get; set; } } } // // IL of Main method // .method private hidebysig static void Main ( string[] args ) cil managed { // Method begins at RVA 0x2050 // Code size 17 (0x11) .maxstack 8 IL_0000: newobj instance void ConsoleApp1.Test::.ctor() IL_0005: ldc.i4.5 IL_0006: newobj instance void [mscorlib]System.Decimal::.ctor(int32) - not so implicit for the compiler IL_000b: callvirt instance void ConsoleApp1.Test::set_Id(valuetype [mscorlib]System.Decimal) IL_0010: ret } // e 现在它通过了,因为列表显然是IEnumerable的,并且在运行时并没有冲突


既然你知道了它是如何工作的,也许你可以找出哪种情况最适合你。

你不能,因为在你的情况下,它是不变性的,但它是相关的

operator=隐式转换为所需的类型MethodBase.Invoke,而不是其他类型

调试代码,在方法调用时放置断点。请确保方法本身具有以下定义- {Void set_IdSystem.Decimal}哪个是明显的对吗?它接受一个decimal类型的参数,而编译器可以为您发挥神奇的作用,允许您在运行时隐式调用它,但情况有所不同

IL说明,显示使用赋值运算符执行操作时发生的情况

using System; using System.Collections.Generic; namespace ConsoleApp1 { class Program { static void Main(string[] args) { var testIntance = new Test(); testIntance.Id = (int)5; } } public class Test { public decimal Id { get; set; } } } // // IL of Main method // .method private hidebysig static void Main ( string[] args ) cil managed { // Method begins at RVA 0x2050 // Code size 17 (0x11) .maxstack 8 IL_0000: newobj instance void ConsoleApp1.Test::.ctor() IL_0005: ldc.i4.5 IL_0006: newobj instance void [mscorlib]System.Decimal::.ctor(int32) - not so implicit for the compiler IL_000b: callvirt instance void ConsoleApp1.Test::set_Id(valuetype [mscorlib]System.Decimal) IL_0010: ret } // e 现在它通过了,因为列表显然是IEnumerable的,并且在运行时并没有冲突


既然您知道了它是如何工作的,也许您可以找出哪种情况最适合您。

为什么要使用字段和方法而不是属性?你有Java背景吗?@itsme86啊,对不起,当然,我指的是属性,不是字段。现在正在修复。十进制不能隐式转换为整数的原因是因为可能会丢失数据。@itsme86我正在将整数转换为小数检查是否有一个好的解决方案

激动。这是为了获取值,但您可以找到如何轻松设置。基本上,您只需在设置之前检查属性类型。为什么要使用字段和方法而不是属性?你有Java背景吗?@itsme86啊,对不起,当然,我指的是属性,不是字段。现在正在修复。十进制不能隐式转换为整数的原因是因为可能会丢失数据。@itsme86我正在将整数转换为小数查看一个好的解决方案。这是为了获取值,但您可以找到如何轻松设置。基本上,您只需在设置之前检查属性类型。@LINQ2Vodka添加了可空值的处理。我只希望您的要求不涉及不可转换的值…^^将枚举强制转换为int和bytes,然后再转换回int和bytes,这是否有效?我知道我可以实现特定的转换,但是C已经在某个地方有了这个功能,我很乐意重新使用它。。。当我们写int a=decimal1时,内部会发生什么;所以它可以正常工作,没有错误吗?啊,还有一个棘手的要求。可以处理,但最好用另一种方法包装。我将很快编辑我的答案,将其包括在内。但如果您还有其他要求,请在主要问题中包括所有要求。^^'艾丽斯,我非常感谢你的帮助。谢谢你花时间在这上面!不客气。^^添加了具有可枚举值处理的更新。作为奖励,我还添加了字符串解析。现在基本的数字类型以及它们的字符串表示形式应该没有问题了注意:转换为整数类型的小数点表示形式将中断,但希望不会发生这种情况。@LINQ2Vodka添加了可空值的处理。我只希望您的要求不涉及不可转换的值…^^将枚举强制转换为int和bytes,然后再转换回int和bytes,这是否有效?我知道我可以实现特定的转换,但是C已经在某个地方有了这个功能,我很乐意重新使用它。。。当我们写int a=decimal1时,内部会发生什么;所以它可以正常工作,没有错误吗?啊,还有一个棘手的要求。可以处理,但最好用另一种方法包装。我将很快编辑我的答案,将其包括在内。但如果您还有其他要求,请在主要问题中包括所有要求。^^'艾丽斯,我非常感谢你的帮助。谢谢你花时间在这上面!不客气。^^添加了具有可枚举值处理的更新。作为奖励,我还添加了字符串解析。现在,基本的数字类型以及它们的字符串表示形式应该不会有问题注意:转换为整数类型的小数点表示形式将中断,但希望不会发生这种情况。您的意思是,从int到decimal的转换/转换会在项目构建时发生吗?也就是说,环境对我有用吗?是的,至少这是我的指示,让我编辑我的答案。这可能是一个好答案,但协方差和逆变与OP的问题无关。你甚至说它不相关,为什么还要加上它?@CamiloTerevinto,因为它是一个有趣的话题,列表和IRnumerable的例子正好说明:你是说从int到decimal的强制转换/转换发生在项目构建期间吗?也就是说,环境对我有用吗?是的,至少这是我的指示,让我编辑我的答案。这可能是一个好答案,但协方差和逆变与OP的问题无关。你甚至说它不相关,为什么还要加上它?@CamiloTerevinto,因为它是一个有趣的话题,列表和IRnumerable的例子正好说明: