【问题标题】:How to create an instance of a child class with a factory builder using generics?如何使用泛型使用工厂构建器创建子类的实例?
【发布时间】:2021-08-21 17:06:49
【问题描述】:

我正在尝试使用父级的工厂构建器创建一个类的实例。

用例基本上是一个库,其中包含从同一类继承的一系列类。 主要目标是减少这些类中的代码量。

我有一个包含两个值的接口,IDualValues<T>

public interface IDualValues<T>
{
    public T FirstValue { get; }
    public T SecondValue { get; }
}

这是所有类的外观示例(更改方法名称)。

public class Foo : IDualValues<string>
{
    public string FirstValue { get; }
    public string SecondValue { get; }

    public Foo(string firstValue, string secondValue)
    {
        FirstValue = firstValue;
        SecondValue = secondValue;
    }

    public static Foo Create(string firstValue, string secondValue)
    {
        return new Foo(firstValue, secondValue);
    }
}

我的意思是我想使用 FactoryBuilder 来做Foo.Create,而不是直接在客户端应用程序中创建一个新实例。

理想的场景将有一个包含所有逻辑的父类:

public class DualValuesBuilder<T, CreatedType> : IDualValues<T>
where CreatedType : class, IDualValues<T>
{
    public T FirstValue { get; init; }
    public T SecondValue { get; init; }

    private DualValuesBuilder(T firstValue, T secondValue)
    {
        FirstValue = firstValue;
        SecondValue = secondValue;
    }

    public static CreatedType Create(T firstValue, T secondValue)
    {
       //Creation here
    }
}   

而其他类将是仅实现该类的空类:

public class Foo : DualValuesBuilder<string, Foo>
{
}

请注意,我将类本身作为通用参数发送给“构建器”。

为了实现我的目标,我创建了一个“助手”类来构建一个继承自 IDualValues&lt;T&gt; 的类。

public class DualValuesBuilderHelper<T> : IDualValues<T>
{
    public T FirstValue { get; init;}
    public T SecondValue { get; init; }

    private DualValuesBuilderHelper(T firstValue, T secondValue)
    {
        FirstValue = firstValue;
        SecondValue = secondValue;
    }

    public static IDualValues<T> Create<ReturnType>(T developerK8SClusterValue, T octopusValue)
    where ReturnType : IDualValues<T>
    {
        return new DualValuesBuilderHelper<T>(developerK8SClusterValue, octopusValue);
    }
}

然后我将DualValuesBuilder 类中的Create 方法更新为如下所示:

public static CreatedType Create(T firstValue, T secondValue)
{
    return (CreatedType)DualValuesBuilderHelper<T>.Create<CreatedType>(firstValue, secondValue);
}

但这给了我一个关于选角的错误: System.InvalidCastException: Unable to cast object of type 'DualValuesBuilderHelper1[System.String]' 输入'Foo'。`

这里有一个代码:https://dotnetfiddle.net/IUrgr1

是的,如果我做了以下工作,但不是我想要达到的目标。

Foo foo = new Foo()
{
    FirstValue = "f1",
    SecondValue = "f2"
};

注意:使用构造器也可以,但我需要使其与Create 一起使用。

即使我创建了显式(或隐式)运算符,转换仍在发生。

我是不是想太多了,有一种简单的方法可以完成我想做的事情吗? 还是做不到?

谢谢。

【问题讨论】:

    标签: c# generics design-patterns static builder-pattern


    【解决方案1】:

    更新,因为我错过了继承工厂方法的细微差别。

    主要将new() 添加到通用约束中会解决您的问题,并且包括CreateType 必须是DualValuesBuilder 的子级:where CreateType : DualValuesBuilder&lt;CreateType, DataType&gt;, new()

    public interface IDualValues<T>
    {
        public T FirstValue { get; init; }
        public T SecondValue { get; init; }
    }
    
    public class Foo : DualValuesBuilder<Foo, string>
    {
    }
    
    public class Foo2 : DualValuesBuilder<Foo2, int>
    {
    }
        
    public class DualValuesBuilder<CreateType, DataType> : IDualValues<DataType>
        where CreateType : DualValuesBuilder<CreateType, DataType>, new()
    {
        public DataType FirstValue { get; init; }
        public DataType SecondValue { get; init; }
    
        public static CreateType Create(DataType firstValue, DataType secondValue)
        {
            return new CreateType
            {
                FirstValue = firstValue,
                SecondValue = secondValue
            };
        }
    }
    

    主要将new() 添加到通用约束中,您将得到解决:where CreatedType : class, IDualValues&lt;T&gt;, new()

    public interface IDualValues<T>
    {
        public T FirstValue { get; init; }
        public T SecondValue { get; init; }
    }
    
    public class Foo : IDualValues<string>
    {
        public string FirstValue { get; init; }
        public string SecondValue { get; init; }
    }
    
    public static class DualValuesBuilder
    {
        public static CreateType Create<CreateType, DataType>(
            DataType firstValue, 
            DataType secondValue)
            where CreateType : class, IDualValues<DataType>, new()
        {
            return new CreateType
            {
                FirstValue = firstValue,
                SecondValue = secondValue
            };
        }
    }
    

    【讨论】:

    • 这种方法的问题是我不能做Foo.Create() 我必须做Foo foo = DataValuesBuilder.Create()
    • @TiGreX 为什么在特定类中需要抽象静态构建器方法?对我来说,这没有多大意义。 @qujck 建议的方法在代码解耦方面更好,并产生更多可测试的代码
    • @AndrewSilver 这种方法的问题是,如果根类型有 10 个属性,则它不可读,它将类似于 Bar.Create(defaultvaluesbuilder.create(), defaultvaluesbuilder.create(),... 很难阅读(例如在 PR 中)如果您将其与Bar.Create(Foo.Create(), Another.Create(),...) 进行比较。最重要的是,您“不会使用类型”,因为一切都是 'IDualVaules`,这将允许您在不编译错误的情况下交换参数。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多