首页 > 解决方案 > 如果需要,在转换 Type 时通过 Ref 设置属性

问题描述

我有一系列复杂的类,我在这里放了一个可以编译和工作的最小示例:

public class DataClass : IMPropertyAsStringSettable
{
    public int num { get; set; }
    public string code { get; set; }
    public PartClass part { get; set; }
    public MemberClass member { get; set; }
    public DataClass()
    {
        part = new PartClass();
        member = new MemberClass();
    }
}
public class PartClass : IMPropertyAsStringSettable
{
    public int seriesNum { get; set; }
    public string seriesCode { get; set; }
}
public class MemberClass : IMPropertyAsStringSettable
{
    public int versionNum { get; set; }
    public SideClass side { get; set; }
    public MemberClass()
    { 
        side = new SideClass();
    }
}
public class SideClass : IMPropertyAsStringSettable
{
    public string firstDetail { get; set; }
    public string secondDetail { get; set; }
    public bool include { get; set; }
}

您看到的接口在与 all 相同的命名空间中实现如下:

public interface IMPropertyAsStringSettable { }

public static class PropertyAsStringSettable
{
    public static void SetPropertyAsString(this IMPropertyAsStringSettable self, string propertyName, string value)
    {
        var property = TypeDescriptor.GetProperties(self)[propertyName];
        var convertedValue = property.Converter.ConvertFrom(value);
        property.SetValue(self, convertedValue);
    }
}

我试图通过实际从字符串中调用属性名称来实现设置“按引用”的属性值类型。我正在尝试但无法解决两个问题。我无法设置“链接”属性值,或者由于我在接口中的错误,实现中的某些内容无法正常工作。其次,同样重要的是,我无法读取和转换属性,即我只有字符串作为值,有时属性是 bool、int、double、DateTime 等。现在,我知道转换是一个大课题,所以我想尝试读取属性类型并通过 try/catch 进行转换,但我做不到。

这就是我想要实现的(基于上面的代码):

        static int Main(string[] args)
    {
        //just initializing the whole thing without
        //setting values to properties
        DataClass myClass = new DataClass()
        {
            part = new PartClass(),
            member = new MemberClass()
            {
                side = new SideClass()
            }
        };

        // here I read from a source names and values.....

        //and I am trying to populate like this:

        myClass.SetPropertyAsString("include", "true"); //this property is in SideClass, and also a bool
        myClass.SetPropertyAsString("seriesNum", "88"); //this property is in PartClass and an int..

直接用作:

        //This should print "True"
        Console.WriteLine("myClass member side include = " + myClass.member.side.include.ToString());

        Console.ReadKey();
        return 0;
    }

我真的希望有人可以提供帮助,我不是这方面的专家;如果您可以提供基于上述内容的工作代码,我将不胜感激。谢谢你们

注意:使用反射可以更好地工作(尝试为我在 JSON 字符串中找到的所有属性分配值)。欢迎任何帮助..

标签: c#propertiestypeof

解决方案


如果属性不是顶级属性,那么我们需要通向目标属性的路径,在我们的例子中路径是member:side:include. 我在这里:用作分隔符。

//top-level property
myClass.SetPropertyAsString("num", "88");

//nested properties
myClass.SetPropertyAsString("member:side:include", "true");
myClass.SetPropertyAsString("part:seriesNum", "15");

然后修改反射代码,使其可以沿着给定的路径前进:

//will handle both nested and top-level properties
public static void SetPropertyAsString(this IMPropertyAsStringSettable self, string propertyName, string value) {
    //current, is in a way, the property iterator
    var current = self;

    var keys = propertyName.Split(":");

    //iterate over keys till the key before last -since the last key is the target property-
    foreach (var key in keys[0..^1]) {
        var property = current.GetType().GetProperty(key);
        current = property.GetValue(current) as IMPropertyAsStringSettable;
    }

    var targetProperty = current.GetType().GetProperty(keys.Last());

    //convert the input value to the data type of the target property, and set it to the target property
    targetProperty.SetValue(current, Convert.ChangeType(value, targetProperty.PropertyType));
}

编辑:对于 C# < 8.0

根据您的评论,您使用的是 C# 版本 < 8.0,其中原始响应中使用的某些功能不可用(例如 range operator ^)。我在 上编译了以下内容.NET 4.7.2C# 7.3并产生了相同的结果。

public static void SetPropertyAsString(this IMPropertyAsStringSettable self, string propertyName, string value) {
    var current = self;
    var keys = propertyName.Split(':');
    for (var i = 0; i < keys.Length - 1; i++) {
        var property = current.GetType().GetProperty(keys[i]);
        current = property.GetValue(current) as IMPropertyAsStringSettable;
    }
    var targetProperty = current.GetType().GetProperty(keys[keys.Length - 1]);
    targetProperty.SetValue(current, Convert.ChangeType(value, targetProperty.PropertyType));
}

推荐阅读