Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/289.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 我应该使用什么样的创作模式?_C#_Design Patterns_Creation Pattern - Fatal编程技术网

C# 我应该使用什么样的创作模式?

C# 我应该使用什么样的创作模式?,c#,design-patterns,creation-pattern,C#,Design Patterns,Creation Pattern,我的课程有两个班;两者都派生自相同的基类 class A : MyBase { internal A(InitVal initVal) } class B : MyBase { internal B(InitVal initVal) } InitVal是通过构造函数注入的另一个类。此类用于内部使用。由于内部构造函数,用户无法直接创建类A和B的实例。相反,我创建了创建这些对象的方法 class Initiator { InitVal initVal; publ

我的课程有两个班;两者都派生自相同的基类

class A : MyBase
{
    internal A(InitVal initVal)
}

class B : MyBase
{
    internal B(InitVal initVal)
}
InitVal
是通过构造函数注入的另一个类。此类用于内部使用。由于内部构造函数,用户无法直接创建类
A
B
的实例。相反,我创建了创建这些对象的方法

class Initiator
{
    InitVal initVal;

    public T CreateObject<T>(ObjectInstance objectInstance) where T : MyBase
    {
        MyBase myBase = null;
        switch(objectInstance)
        {
            case ObjectInstance.A:
                myBase = new A(initVal);
                break;
            case ObjectInstance.B:
                myBase = new B(initVal);
                break;
        }
        return (T)myBase;
    }
    ...
}
第一个用于
T
通用值,第二个用于枚举值。 我想避免这种情况

2) 由于用户必须两次指定对象的类型,所以有可能出错

A a = initiator.CreateObject<A>(ObjectInstance.B);

但是,我不确定如何实现此方法以创建适当的实例。

由于您将该方法指定为泛型方法,我希望您可能在编译期间已经知道要获取的类型。。所以我会选择这样的方式:

class Initiator
{ 
    public T CreateObject<T>(ObjectInstance objectInstance) where T : MyBase, new()
    {
        T newInstance = new T();
        newInstance.Value = initVal;

        return newInstance;
    }
...
}
这不仅更干净、更短,而且更不容易出错,因为每次添加新类型时都不需要同时编辑枚举和方法体。但是,对于特定于子类型的逻辑,它的灵活性较低

注意:如果您真的想要像现在这样使用带参数的构造函数,您仍然可以使用这种方法,但是您需要使用反射(检查激活器)或lambdas

当然,只有在编译期间决定类型,或者只想将此决定委托给第三方库时,这才有意义,例如:

switch(chosenType){
case ObjectInstance.A:
    instance = initiator.CreateObject<A>();
    ...

在我看来,你并没有从尝试使这种通用性中获得任何优势。您需要知道调用站点返回值的具体类型

因此,为什么不简单一点,就这么做呢

public class Initiator
{
    InitVal initVal;

    public A CreateA()
    {
        return new A(initVal);
    }

    public B CreateB()
    {
        return new B(initVal);
    }
}

代码的一个明显问题是枚举,这是不必要的,因为
typeof(T)
已经为您提供了适当的类型:

class Initiator
{
    readonly Dictionary<Type, Func<MyBase>> _dict = new Dictionary<Type, Func<MyBase>>();

    internal Initiator(InitVal initVal)
    {
        // initialize your "service locator".
        // it's cool that different types can have different constructors,
        // and people who call CreateObject don't need to know this.
        _dict[typeof(A)] = (Func<MyBase>)(() => new A(initVal));
        _dict[typeof(B)] = (Func<MyBase>)(() => new B(initVal, someOtherStuff));
    }

    public T CreateObject<T>() where T : MyBase
    {
        var ctor = _dict[typeof(T)];
        return (T)ctor();
    }
}
类启动器
{
只读词典_dict=新词典();
内部启动器(InitVal InitVal)
{
//初始化“服务定位器”。
//很酷,不同类型可以有不同的构造函数,
//调用CreateObject的人不需要知道这一点。
_dict[typeof(A)]=(Func)(()=>newa(initVal));
_dict[typeof(B)]=(Func)(()=>newb(initVal,someOtherStuff));
}
public T CreateObject(),其中T:MyBase
{
变量向量=_dict[typeof(T)];
返回(T)向量();
}
}
或者,如果不知道类型,可以传递枚举,但返回类型应该是接口/基类(最好是接口):

//更可能的情况是,您可能不需要泛型方法
公共IMyBase CreateObject(ObjectInstance ObjectInstance)
{
//当然,字典会将枚举值映射到Func
变量向量=_dict[反对];
返回向量();
}
现在您有了一个简单的“穷人”DI类,名为
Initiator
,因此我想知道您的DI框架(注入
InitVal
)是否也能注入
a
B
实例。这可能是真的,因为DI纯粹主义者会告诉你没有工厂的地方,你的代码中没有
new
关键字


顺便说一句,
ObjectInstance
对于枚举来说是一个非常非常糟糕的名字。

我是通过以下方式实现的:

class A : IMyType
{
    internal A(InitVal initVal)
}

class B : IMyType
{
    internal B(InitVal initVal)
}

class Initiator
{
    InitVal initVal = .....;

    public T CreateObject<T>() where T : IMyType
    {
        IMyType myType = null;
        if(typeof(T) == typeof(A))
            myType = new A(initVal);
        else if(typeof(T) == typeof(B))
            myType = new B(initVal);
        else
            throw new MyException("Type is not configured.");
        return (T)myType;
    }
    ...
}
A类:IMyType
{
内部A(InitVal InitVal)
}
B类:IMyType
{
内部B(InitVal InitVal)
}
类发起人
{
InitVal InitVal=。。。。。;
public T CreateObject(),其中T:IMyType
{
IMyType myType=null;
如果(类型(T)=类型(A))
myType=新的A(初始值);
否则如果(类型(T)=类型(B))
myType=新的B(初始值);
其他的
抛出新的MyException(“类型未配置”);
返回(T)myType;
}
...
}

这解决了我在问题中提到的问题。但是,它带来了新的问题这违反了SOLID的开闭原则。最后一个
else
块处理手动错误(如果有)。无论如何,它只适用于我的具体情况一般不推荐。

如果您不能更改
A
B
,那么人们仍然可以创建
A
和/或
B
的实例。那么,为什么还要为整个
Initiator
类操心呢?如果您不想让API的使用者实例化类,我会将构造函数设置为内部的。您的代码没有那么难看,这正是工厂方法所做的:它基于某个不同的值创建类型的实例,在您的例子中是enum。如果添加了一个新类,您也可以通过一个配置文件来实现这一点,这样就不必更改源代码。唯一难看的是缩进。当前
Initiator
类的一个问题是,用户现在必须构造
Initiator
的实例,并且必须以某种方式设置
initVal
。字段是如何设置的?另一种方式,稍微干净一点,就是简单地有两种方法来创建a或B,第三方方法将选择其中一种。或者将其作为一个方法实现,并通过指定泛型类型来确定类型。否则,它只是一个工厂方法,看起来很好。PS.使启动器static和initVal最有可能成为参数:P@MatthewWatson,它并不是一个真正的工厂方法,因为工厂方法根据定义决定类型,那些工厂方法不决定任何东西,只是简单地构造始终相同的类型。但是,如果命名有效,谁会在乎呢
switch(chosenType){
case ObjectInstance.A:
    instance = initiator.CreateObject<A>();
    ...
A CreateObjectA(InitVal initValue){
     return new A(initValue);
}
B CreateObjectB(InitVal initValue){ ...
public class Initiator
{
    InitVal initVal;

    public A CreateA()
    {
        return new A(initVal);
    }

    public B CreateB()
    {
        return new B(initVal);
    }
}
class Initiator
{
    readonly Dictionary<Type, Func<MyBase>> _dict = new Dictionary<Type, Func<MyBase>>();

    internal Initiator(InitVal initVal)
    {
        // initialize your "service locator".
        // it's cool that different types can have different constructors,
        // and people who call CreateObject don't need to know this.
        _dict[typeof(A)] = (Func<MyBase>)(() => new A(initVal));
        _dict[typeof(B)] = (Func<MyBase>)(() => new B(initVal, someOtherStuff));
    }

    public T CreateObject<T>() where T : MyBase
    {
        var ctor = _dict[typeof(T)];
        return (T)ctor();
    }
}
// this is more likely, you probably don't need a generic method
public IMyBase CreateObject(ObjectInstance objectInstance)
{
    // the dictionary would map enum value to Func<IMyBase>, of course
    var ctor = _dict[objectInstance];
    return ctor();
}
class A : IMyType
{
    internal A(InitVal initVal)
}

class B : IMyType
{
    internal B(InitVal initVal)
}

class Initiator
{
    InitVal initVal = .....;

    public T CreateObject<T>() where T : IMyType
    {
        IMyType myType = null;
        if(typeof(T) == typeof(A))
            myType = new A(initVal);
        else if(typeof(T) == typeof(B))
            myType = new B(initVal);
        else
            throw new MyException("Type is not configured.");
        return (T)myType;
    }
    ...
}