C#什么';将构造函数参数传递给私有成员的目的是什么?

C#什么';将构造函数参数传递给私有成员的目的是什么?,c#,C#,人们总是定义一个私有成员,然后将构造函数参数值传递给它,就像设计a一样。但我不明白这样做的好处,因为私有成员没有副本(我指的是深度克隆),所以为什么不直接使用设计B呢 设计A: public class ClassA { public int num {get;set;} } public class ClassB { private ClassA a ; public ClassB(ClassA _a) { a = _a; } } 设计

人们总是定义一个私有成员,然后将构造函数参数值传递给它,就像设计a一样。但我不明白这样做的好处,因为私有成员没有副本(我指的是深度克隆),所以为什么不直接使用设计B呢

设计A:

public class ClassA
{
    public int num {get;set;}
}

public class ClassB 
{

    private ClassA a ;

    public ClassB(ClassA _a) {
        a = _a;
    }
}
设计B:

public class ClassA
{
    public int num {get;set;}
}

public class ClassB 
{
     public ClassA a {get; set;}
}

设计A和设计B都很有用,但适用于不同的情况

设计A的一个优点是,有一个用实例构造函数的参数初始化的私有实例字段,只要
ClassB
对象存在,字段
A
的“标识”就不会被任何人(类本身之外的任何人)更改。所以你可以据此做出假设。这给了设计A一种设计B所缺乏的浅层不变性


也可以使用自动属性(而不是显式字段)进行设计;只需设置
自动属性>代码>私下< /COL> > 

< P>我熟悉C++,比C语言大,但如果我理解正确,就是用成员函数调用它。一个与你类似的问题在这里得到了很好的回答:


答案所指的工厂模式是问题的一部分;有关用例,请参阅链接

我看到设计A只有一个带一个参数的构造函数,没有默认构造函数。因此,这种设计强制规定,只能通过传递类A的对象来创建对象类B。在创建类B对象时,如果不知道类A的值,则不能为类B创建对象,至少必须显式传递null


在设计B中,没有这样的规则强制,因为存在默认构造函数,您可以在创建类B后推迟类a对象的分配。

这两种设计都可以,具体取决于情况。 两者都是一种依赖注入,有4种类型的依赖注入:

  • 建造师:设计A
  • 物业:设计B
  • 调用方法时传递依赖项
  • 环境语境
我不会详细说明这里不使用的两种类型,但它们将与Mark Seeman的.NET中的依赖项注入中的其他两种一起讨论

设计是构造函数依赖项注入,其优点是它是最简单的依赖项注入类型。依赖项注入的第一选择就是这个。如果注入的依赖项没有良好的默认值,那么这是完全正确的。构建对象后,依赖关系不会更改。 默认使用这种依赖项注入会导致构造函数中有许多参数的类。这被称为注入依赖。 我遵循Mark Seeman书中的相同代码,因此我有如下代码:

public class TekeningViewModel : ViewModelBase
{
    private readonly IUnitOfWorkAsync _unitOfWork;

    TekeningViewModel(IUnitOfWorkAsync _unitOfWork){
        if (unitOfWork == null)
        {
            throw new ArgumentNullException("unitOfWork");
        }
        _unitOfWork = unitOfWork;
    }
}
Aclass{
    void methodinjection(int a, Bclass b){
        ///
    }
}
Aclass{
    void methodinjection(int a){
        AmbientContext::getInstance().getBclass();
        //....
    }
}
请注意成员的
readonly
类型,并检查所传递的参数是否为null

如果注入的依赖项有一个良好的默认值,那么设计B是理想的。因为这样可能不会调用属性设置器,并且会在构造函数中自动设置一个良好的默认值。设计B也是一种
策略模式
。在设计A中,不打算动态更改为不同的依赖项,在构建之后,也就是说,使用设计B,您可以这样做。在多线程环境中,这增加了以安全方式实现它的复杂性。如前所述,设计B比设计A产生更多的代码。 选择一个好的默认值时必须小心,因为您不希望仅仅因为默认值而创建对程序集的依赖关系。任何使用您的类的人,即使是那些不使用该默认值的人,仍然会对包含该默认值的程序集具有依赖性

进行依赖项注入的第三种方法如下:

public class TekeningViewModel : ViewModelBase
{
    private readonly IUnitOfWorkAsync _unitOfWork;

    TekeningViewModel(IUnitOfWorkAsync _unitOfWork){
        if (unitOfWork == null)
        {
            throw new ArgumentNullException("unitOfWork");
        }
        _unitOfWork = unitOfWork;
    }
}
Aclass{
    void methodinjection(int a, Bclass b){
        ///
    }
}
Aclass{
    void methodinjection(int a){
        AmbientContext::getInstance().getBclass();
        //....
    }
}
进行依赖项注入的第四种方法如下:

public class TekeningViewModel : ViewModelBase
{
    private readonly IUnitOfWorkAsync _unitOfWork;

    TekeningViewModel(IUnitOfWorkAsync _unitOfWork){
        if (unitOfWork == null)
        {
            throw new ArgumentNullException("unitOfWork");
        }
        _unitOfWork = unitOfWork;
    }
}
Aclass{
    void methodinjection(int a, Bclass b){
        ///
    }
}
Aclass{
    void methodinjection(int a){
        AmbientContext::getInstance().getBclass();
        //....
    }
}
设计的复杂性不断从1增加到4。
类型4看起来像是一个反模式的单例,所以它是我最不喜欢的依赖注入类型,但它确实有它的优点。因为环境上下文本身应该使用前两种类型的依赖注入来创建,因此具有很大的灵活性。非常适合将横切关注点传递给应用程序,而不会用太多参数污染构造函数。

感谢您的详细回答。在我看来,设计A最有价值的部分是减少多线程环境中的复杂性。