从基础对象转换为派生对象,同时具有巨大的构造函数定义

时间:2018-10-29 22:06:37

标签: c# type-conversion

假设我有一个课程 BaseClass

class BaseClass
{
    public int PropertyOne { get; }

    public string PropertyTwo { get; }

    public BaseClass(int propertyOne, string propertyTwo)
    {
        PropertyOne = propertyOne;
        PropertyTwo = propertyTwo;
    }
}

还有一个子类 DerivedClass

class DerivedClass : BaseClass
{
    public DerivedClass(int propertyOne, string propertyTwo) : base(propertyOne, propertyTwo)
    {
    }

    public void DerivedMethod()
    {

    }

    public static DerivedClass ConvertFromBaseClass(BaseClass baseClass)
    {
        return new DerivedClass(baseClass.PropertyOne, baseClass.PropertyTwo); // <-- ?
    }
}

是否可以实现 DerivedClass.ConvertFromBaseClass 而不显式调用构造函数,也许可以使用反射?在现实中,这些类将50多个参数传递给构造函数,因此在converter方法中编写构造函数调用非常容易出错,而且很容易出错。

3 个答案:

答案 0 :(得分:0)

简短的答案是:不。给定类的呈现方式,没有编译器关键字或语法糖可以自动进行映射。

更长的答案是:首先您会遇到此问题,这表明您的类可能不是以最佳方式构造的。如果您一开始就可以自由更改类,则可能会得到想要的结果。

例如,如果您有50多个构造函数参数,这些参数始终完全相同,并且被许多不同的子类使用,那么为什么不声明一个包含所有这些参数和属性的类,并使其成为基类将其中一个作为单个构造函数参数?

public DerivedClass(BaseClassArguments args) : base(args)

您可能还想重新考虑类对继承的使用是否有意义。例如,如果要创建所有这些子类是因为它们对基类中的数据应用了不同的逻辑,那么也许它们都应该实现一个接口,该接口具有以该数据作为参数的方法。

或者,您可以使这些类将“基”类用作构造函数参数,并将其保留为字段或属性,而不是扩展基类。这称为使用合成而不是继承。

答案 1 :(得分:0)

根据情况,您也许可以重组代码以使用Mixins。

首先,您从基类中提取一个接口。只需按一下按钮,Resharper就可以做到这一点:

class BaseClass : Interface
{
    public int PropertyOne { get; }

    public string PropertyTwo { get; }

    public BaseClass(int propertyOne, string propertyTwo)
    {
        PropertyOne = propertyOne;
        PropertyTwo = propertyTwo;
    }
}

interface Interface
{
    int PropertyOne { get; }

    string PropertyTwo { get; }
 }

然后,您可以从派生类中添加一个字段,以实现接口,而不是从基类继承。 Resharper也可以在实例中执行此操作:

class DerivedClass : Interface
{
    private readonly Interface _interface;

    public DerivedClass(Interface interface) => _interface = Interface;

    public int PropertyOne => _interface.PropertyOne;

    public string PropertyTwo => _interface.PropertyTwo;
}

然后,您只需将基类的实例传递给构造函数。

这有两个优点。首先,这意味着您不必在构造函数和转换器中重新实现逻辑即可处理所有50个参数。实际上,Resharper可以为您完成所有逻辑。

第二,这意味着您不必在每次从基类创建派生类型时复制50个参数(32位运行时大约200个字节,64位运行400个字节)。

根据您的情况,这可能是不合适的模式

答案 2 :(得分:0)

并非我建议这样做,但是您可以使用反射将属性自动映射到构造函数参数。

此示例显示了如何使用三个属性来匹配三个构造函数参数来构造一个类,但是它可以轻松映射50。

static class ExtensionMethods
{
    public static string ToPascalCase(this string camelCase)
    {
        return string.IsNullOrEmpty(camelCase)
            ? camelCase
            : camelCase.First().ToString().ToUpper() + camelCase.Substring(1);
    }
}

class BaseClass
{
    public string Able { get; set; }
    public int Baker { get; set; }
    public DateTime Candy { get; set;}

    static public DerivedClass CreateDerived(BaseClass source)
    {
        var constructor = typeof(DerivedClass).GetConstructors()[0];
        var parameters = constructor.GetParameters();
        var arguments = parameters.Select
            (
                param => source
                    .GetType()
                    .GetProperty(param.Name.ToPascalCase())
                    .GetValue(source, null)
            );
        var instance = constructor.Invoke(arguments.ToArray());
        Console.WriteLine("{0} arguments found", arguments.Count());
        return (DerivedClass)instance;
    }
}

class DerivedClass : BaseClass
{
    public DerivedClass(string able, int baker, DateTime candy)
    {
        Able = able;
        Baker = baker;
        Candy = candy;
    }
}

public class Program
{
    public static void Main()
    {
        var b = new BaseClass
        {
            Able = "Able!",
            Baker = 2,
            Candy = new DateTime(2018,10,31)
        };

        var d = BaseClass.CreateDerived(b);

        Console.WriteLine("Able={0} Baker={1} Candy={2}", d.Able, d.Baker, d.Candy);
    }
}

输出:

3 arguments found
Able=Able! Baker=2 Candy=10/31/2018 12:00:00 AM

Link to working example on DotNetFiddle