C# 我如何重构这个C代码

C# 我如何重构这个C代码,c#,refactoring,C#,Refactoring,基本上,我有一个方法,它接受一个对象并根据传入的对象设置另一个对象属性 e、 g: 现在这个方法有53行,因为有很多属性需要设置。这个方法对我来说似乎太长了,但我正在努力想办法把它打破 一种选择是尝试将相似的属性分组,并将对象作为对设置这些相似属性的不同方法的引用传递,但这似乎并不适合我 或者我可以为MyClass2创建一个构造函数,它接受MyClass1,但这似乎也不对 无论如何,我欢迎一些建议 编辑:好的,谢谢你的回复,我必须提供更多信息,属性名称不一样,我还需要调用一些转换方法。因为这一点

基本上,我有一个方法,它接受一个对象并根据传入的对象设置另一个对象属性

e、 g:

现在这个方法有53行,因为有很多属性需要设置。这个方法对我来说似乎太长了,但我正在努力想办法把它打破

一种选择是尝试将相似的属性分组,并将对象作为对设置这些相似属性的不同方法的引用传递,但这似乎并不适合我

或者我可以为MyClass2创建一个构造函数,它接受MyClass1,但这似乎也不对

无论如何,我欢迎一些建议

编辑:好的,谢谢你的回复,我必须提供更多信息,属性名称不一样,我还需要调用一些转换方法。因为这一点,再加上性能上的冲击,反射就不好了。我想也是出于同样的原因

代码的一个真实示例:

    private ReportType GetReportFromItem(SPWeb web, SPListItem item)
            {
                ReportType reportType = new ReportType();
                reportType.ReportID = int.Parse(item["Report ID"].ToString());
                reportType.Name = item["Title"].ToString();
                reportType.SourceLocation = FieldHelpers.GetUri(item["Source Location"]);
                reportType.TargetLocation = FieldHelpers.GetUri(item["Document Library"]);
                SPFieldUserValue group1 = 
                    new SPFieldUserValue(web, FieldHelpers.GetStringFieldValue(item, "Security Group 1"));
                reportType.SecurityGroup1 = group1.LookupValue;
                SPFieldUserValue group2 =
                    new SPFieldUserValue(web, FieldHelpers.GetStringFieldValue(item, "Security Group 2"));
                reportType.SecurityGroup2 = group2.LookupValue;
                SPFieldUserValue group3 =
                    new SPFieldUserValue(web, FieldHelpers.GetStringFieldValue(item, "Security Group 3"));
                reportType.SecurityGroup3 = group3.LookupValue;
                SPFieldUserValue group4 =
                    new SPFieldUserValue(web, FieldHelpers.GetStringFieldValue(item, "Security Group 4"));
// More code
//...
//...
}

听起来像是用反射来完成的工作。可能有这样一种方法:

private void SetProperties<T>(List<T> objects, List<Tuple<string, object>> propsAndValues) where T:<your_class>
        {
            Type type = typeof(T);
            var propInfos = propsAndValues.ToDictionary(key => type.GetProperty(key.Item1, BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance | BindingFlags.SetProperty), elem => elem.Item2);

            objects.AsParallel().ForAll(obj =>
                {
                    obj.SetProps(propInfos);                                  
                });

        }

public static void SetProps<T>(this T obj, Dictionary<PropertyInfo, object> propInfos) where T : <your_class>
        {
            foreach (var propInfo in propInfos)
            {
                propInfo.Key.SetValue(obj, propInfo.Value, null);
            }            
        }
private void SetProperties(列出对象、列出属性和值),其中T:
{
类型=类型(T);
var propinfo=propsAndValues.ToDictionary(key=>type.GetProperty(key.Item1,BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance | BindingFlags.SetProperty),elem=>elem.Item2);
objects.aspallel().ForAll(obj=>
{
对象SetProps(propInfos);
});
}
公共静态void SetProps(这是T obj,Dictionary propInfos),其中T:
{
foreach(propInfos中的变量propInfo)
{
propInfo.Key.SetValue(对象,propInfo.Value,null);
}            
}

我想到了一些这样做的策略,它们各有优缺点。另外,我对它不熟悉,但与您的问题相关联的工具听起来也可能是一个很好的解决方案。(同样,如果有任何方法从同一个类中派生类,或者将结构本身存储在一个结构中而不是直接在类中,这些看起来也是要考虑的事情。) 反射 这一点在本报告中提到。但是,我不确定我是否完全理解该答案中函数的预期用途,因为我没有看到GetValue调用,也没有看到两种类型之间的任何映射。此外,我可以看到您可能希望创建一些东西,以允许两个不同的名称映射到另一个名称,或者在两种类型之间进行转换。对于一个相当通用的解决方案,我可能会采用如下方法:

private void SetProperties<T>(List<T> objects, List<Tuple<string, object>> propsAndValues) where T:<your_class>
        {
            Type type = typeof(T);
            var propInfos = propsAndValues.ToDictionary(key => type.GetProperty(key.Item1, BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance | BindingFlags.SetProperty), elem => elem.Item2);

            objects.AsParallel().ForAll(obj =>
                {
                    obj.SetProps(propInfos);                                  
                });

        }

public static void SetProps<T>(this T obj, Dictionary<PropertyInfo, object> propInfos) where T : <your_class>
        {
            foreach (var propInfo in propInfos)
            {
                propInfo.Key.SetValue(obj, propInfo.Value, null);
            }            
        }
  • 使用一个或多个方法创建扩展类,这些方法将基于相同的属性名和/或预定义的配置对象使用反射进行复制
  • 对于具有不具有相同名称的属性的每对类型,创建一个配置对象,将名称映射到彼此
  • 对于不希望复制的属性,请创建一个包含要忽略的名称列表的配置对象
  • 实际上,我并不认为在构造函数中将一个类传递给另一个类有什么不好的地方,如果它的目的是复制属性的话,那么它看起来更像是一个风格问题,而不是任何硬性的东西
示例代码 要复制到的类:

public class MyClass2
{
    public int Property1 { get; set; }
    public int Property2 { get; set; }
    public string Property3WithOtherName { get; set; }
    public double Property4 { get; set; }
    public string Property5WithDifferentName { get; set; }

    public string TestIntToString { get; set; }
    public int TestStringToInt { get; set; }
}

public class MyClass3
{
    public int Prop1 { get; set; }
    public int Prop2 { get; set; }
    public string Prop3OtherName { get; set; }
    public double Prop4 { get; set; }
    public string Prop5DiffName { get; set; }
    public string PropOnlyClass3 { get; set; }
    public string[] StringArray { get; set; }
}
要复制的类,带有到其他对象的映射信息:

public class MyClass
{
    public int Property1 { get; set; }
    public int Property2 { get; set; }
    public string Property3 { get; set; }
    public double Property4 { get; set; }
    public string Property5 { get; set; }

    public double PropertyDontCopy { get; set; }
    public string PropertyOnlyClass3 { get; set; }
    public int[] PropertyIgnoreMe { get; set; }

    public string[] StringArray { get; set; }

    public int TestIntToString { get; set; }
    public string TestStringToInt { get; set; }

    # region Static Property Mapping Information
        // this is one possibility for creating and storing the mapping
       // information: the class uses two dictionaries, one that links 
       // the other type with a dictionary of mapped properties, and 
       // one that links the other type with a list of excluded ones.
        public static Dictionary<Type, Dictionary<string, string>>
            PropertyMappings =
                new Dictionary<Type, Dictionary<string, string>>
                {
                    {
                        typeof(MyClass2),
                        new Dictionary<string, string>
                        {
                            { "Property3", "Property3WithOtherName" },
                            { "Property5", "Property5WithDifferentName" },
                        }
                    },
                    {
                        typeof(MyClass3),
                        new Dictionary<string, string>
                        {
                            { "Property1", "Prop1" },
                            { "Property2", "Prop2" },
                            { "Property3", "Prop3OtherName" },
                            { "Property4", "Prop4" },
                            { "Property5", "Prop5DiffName" },
                            { "PropertyOnlyClass3", "PropOnlyClass3" },
                        }
                    },
                };

        public static Dictionary<Type, List<string>>
            UnmappedProperties =
                new Dictionary<Type, List<string>>
                {
                    {
                        typeof(MyClass2),
                        new List<string> 
                            {
                                "PropertyDontCopy",
                                "PropertyOnlyClass3",
                                "PropertyIgnoreMe"
                            }
                    },
                    {
                        typeof(MyClass3),
                        new List<string> 
                            {
                                "PropertyDontCopy", 
                                "PropertyIgnoreMe"
                            }
                    }
                };

        // this function pulls together an individual property mapping
        public static Tuple<Dictionary<string, string>, List<string>>
            MapInfo<TOtherType>()
            {
                return 
                    new Tuple<Dictionary<string,string>,List<string>>
                    (
                        PropertyMappings[typeof(TOtherType)],
                        UnmappedProperties[typeof(TOtherType)]
                    );
            }

    #endregion
}
输出:

-------------------------------------
Copying: Test1 to Test2
-------------------------------------
Copying MyClass to MyClass2
    (Int32)Property1 := (Int32)Property1 == 1
    (Int32)Property2 := (Int32)Property2 == 2
    (String)Property3WithOtherName := (String)Property3 == 'Property3String'
    (Double)Property4 := (Double)Property4 == 4
    (String)Property5WithDifferentName := (String)Property5 == 'Property5String'
    (String)TestIntToString := (Int32)TestIntToString == '123456'
    (Int32)TestStringToInt := (String)TestStringToInt == 654321
-------------------------------------
Copying: Test1 to Test3
-------------------------------------
Copying MyClass to MyClass3
    (Int32)Prop1 := (Int32)Property1 == 1
    (Int32)Prop2 := (Int32)Property2 == 2
    (String)Prop3OtherName := (String)Property3 == 'Property3String'
    (Double)Prop4 := (Double)Property4 == 4
    (String)Prop5DiffName := (String)Property5 == 'Property5String'
    (String)PropOnlyClass3 := (String)PropertyOnlyClass3 == 'Class3OnlyString'
    (String[])StringArray := (String[])StringArray == System.String[]
-------------------------------------
Done
-------------------------------------
注意:如果您使用复制回另一个方向,或者希望(例如)使用代码生成器创建映射,那么您可能希望将映射放在另一个单独的静态变量中,由这两种类型映射,而不是将映射直接存储在类中。如果要反转方向,可能只需在现有代码中添加一个标志,使您可以反转表和传递的类型的含义,尽管我建议将对TToType和TFromType的引用更改为TType1和TType2

为了让代码生成器生成映射,可能更容易将其分离到具有泛型的两个类型参数的单独类中,这样您就不必担心将这些定义直接放在类中;在编写代码时,我对如何做到这一点感到困惑,但我确实认为这可能是一个更灵活的选择。(另一方面,这可能意味着需要更大的整体结构,这就是为什么我像一开始那样打破了它。)这种方式的另一个优点是,你不一定需要一次创建所有东西,你可以想象对成员变量的更改,让你根据需要动态创建它们,可能通过函数param或对象上的附加接口

代码生成 这可以单独使用,也可以作为工具与下面两种方法中的任何一种结合使用。但是您可以创建一个CSV或使用其他方法保存映射数据,然后您可以创建类模板(在单独的文件或代码中),其中包含占位符(例如,
${PROPERTIES\u LIST}
),您可以使用这些占位符执行
.Replace()
操作,或者,您可以在下一节中创建用于生成类型化数据集的
.xsd
文件的模板,从列表中生成.xsd的各行,或者,最后您可以创建保存映射信息的结构,如我在反射部分中给出的解决方案

我过去做过一件非常方便的事情,那就是创建一个具有表结构的类型化数据集,它可以保存生成代码所需的所有信息,并使用旧的.NET2.0版本的GridView允许我在其中输入数据。但即使只是一个简单的XML文档或CSV文件
        Test1.Property1 = 1;
        Test1.Property2 = 2;
        Test1.Property3 = "Property3String";
        Test1.Property4 = 4.0;
        Test1.Property5 = "Property5String";

        Test1.PropertyDontCopy = 100.0;
        Test1.PropertyIgnoreMe = new int[] { 0, 1, 2, 3 };
        Test1.PropertyOnlyClass3 = "Class3OnlyString";
        Test1.StringArray = new string[] { "String0", "String1", "String2" };

        Test1.TestIntToString = 123456;
        Test1.TestStringToInt = "654321";

        Console.WriteLine("-------------------------------------");
        Console.WriteLine("Copying: Test1 to Test2");
        Console.WriteLine("-------------------------------------");

        MyClass2 Test2 = new MyClass2();
        Test2.CopyFrom(Test1);

        Console.WriteLine("-------------------------------------");
        Console.WriteLine("Copying: Test1 to Test3");
        Console.WriteLine("-------------------------------------");

        MyClass3 Test3 = new MyClass3();
        Test3.CopyFrom(Test1);

        Console.WriteLine("-------------------------------------");
        Console.WriteLine("Done");
        Console.WriteLine("-------------------------------------");
    }
}
-------------------------------------
Copying: Test1 to Test2
-------------------------------------
Copying MyClass to MyClass2
    (Int32)Property1 := (Int32)Property1 == 1
    (Int32)Property2 := (Int32)Property2 == 2
    (String)Property3WithOtherName := (String)Property3 == 'Property3String'
    (Double)Property4 := (Double)Property4 == 4
    (String)Property5WithDifferentName := (String)Property5 == 'Property5String'
    (String)TestIntToString := (Int32)TestIntToString == '123456'
    (Int32)TestStringToInt := (String)TestStringToInt == 654321
-------------------------------------
Copying: Test1 to Test3
-------------------------------------
Copying MyClass to MyClass3
    (Int32)Prop1 := (Int32)Property1 == 1
    (Int32)Prop2 := (Int32)Property2 == 2
    (String)Prop3OtherName := (String)Property3 == 'Property3String'
    (Double)Prop4 := (Double)Property4 == 4
    (String)Prop5DiffName := (String)Property5 == 'Property5String'
    (String)PropOnlyClass3 := (String)PropertyOnlyClass3 == 'Class3OnlyString'
    (String[])StringArray := (String[])StringArray == System.String[]
-------------------------------------
Done
-------------------------------------
MyClassDataRow Object1 = MyDataSet.MyClassTable.NewRow();

Object1.Prop1 = 123;
Object2.Prop2 = "Hello Dataset";
// etc...

MyClass2DataRow Object2 = MyDataSet.MyClass2Table.NewRow();

foreach (DataColumn ColumnName in MyClassTable.Columns) 
    Object2[ColumnName] = Object1[ColumnName];