C#如何为对象(多个类)设置非用户输入的变量值/实例

C#如何为对象(多个类)设置非用户输入的变量值/实例,c#,class,variables,object,instance,C#,Class,Variables,Object,Instance,我正在做一个简单的练习项目,它按预期工作。然而,这让我意识到,我对一个重要的领域还很模糊,我想更彻底地了解这个领域。我快速创建了一个对象(或实例?)。程序要求用户输入三个变量的值(在主(程序)类中)。这些值/实例被分配给aPay。工作时间名称、工作时间和付款率。我有许多常量和变量,它们的计算值在Pay类中实例化/计算。这些是非用户输入的值。我想尽快将每个值发送到对象,类似于对用户输入值所做的操作。我知道它在这个程序中不是必需的,但是它在更复杂的程序中很有用,我想了解如何实现这一点。我不想再输出任

我正在做一个简单的练习项目,它按预期工作。然而,这让我意识到,我对一个重要的领域还很模糊,我想更彻底地了解这个领域。我快速创建了一个对象(或实例?)。程序要求用户输入三个变量的值(在主(程序)类中)。这些值/实例被分配给aPay。工作时间名称、工作时间和付款率。我有许多常量和变量,它们的计算值在Pay类中实例化/计算。这些是非用户输入的值。我想尽快将每个值发送到对象,类似于对用户输入值所做的操作。我知道它在这个程序中不是必需的,但是它在更复杂的程序中很有用,我想了解如何实现这一点。我不想再输出任何东西或改变程序的功能,我只想将变量(GrossPay、NetPay、Fitax、FedTax、StateTax和HealthIns)的值分配给aPay对象。(我不确定我是否会使用变量(grossPay、netPay、fedTax、Fitax等,而不是我刚才列出的属性)

我知道我可以通过尽快支付=新的工资(工作时间、工作小时数、工资率、总工资、净工资、联邦税、联邦税、联邦税、联邦税、州税、健康保险)来做到这一点

或者类似的东西

aPay={工作时间、工作小时数、工资率、总工资等}

有人能解释一下我是如何做到这一点的,并提供具体使用我的代码的示例代码吗?我将在下面列出代码:

class MainClass
{
    public static void Main(string[] args)
    {


        Header();
        Directions();

        Pay aPay = new Pay();
        Write("**********************************\n");
        Write("Enter name:  ");
        aPay.WorkerName = ReadLine();

        Write("Enter hours: ");
        aPay.HoursWorked = double.Parse(ReadLine());

        Write("Enter rate:  ");
        aPay.RateOfPay = double.Parse(ReadLine());
        Write("**********************************\n");

        //
        //
        //
        //
        //

        WriteLine(aPay.ToString());

        ReadLine();
    }

    private static void Header()
    {
        WriteLine("*************************************************************");
        WriteLine("\t Pay");
        WriteLine("\t Calculate Net Pay");
        WriteLine("\t Matt Craig");
        WriteLine("\t " + DateTime.Today);
        WriteLine("*************************************************************");
    }

    private static void Directions()
    {
        WriteLine("This program will determine pay.");
        WriteLine(" ");
        WriteLine("You will be asked to enter hours worked" 
                  + "\n and rate of pay.");
        WriteLine(" ");
        WriteLine("*************************************************************");
    }
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~下面是付费课程~~~~~~~~~~~~~~~~~~~~~

public class Pay
{
    private string workerName;
    private double hoursWorked;
    private double rateOfPay;
    private double grossPay;
    private double netPay;
    private double FICA_TAX = 0.0765;
    private double FED_TAX = 0.20;
    private double STATE_TAX = 0.10;
    private double HEALTH_INS = 0.07;
    private double ficaTax;
    private double fedTax;
    private double stateTax;
    private double healthIns;

    public Pay()
    {

    }

    public string WorkerName
    {
        set
        {
            workerName = value;
        }
    }

    public double HoursWorked
    {
        set
        {
            hoursWorked = value;
        }
    }

    public double RateOfPay
    {
        set
        {
            rateOfPay = value;
        }
    }

    private double GrossPay
    {
        get
        {
            grossPay = Math.Round((hoursWorked * rateOfPay), 2, MidpointRounding.AwayFromZero);
            return grossPay;
        }
    }

    private double NetPay
    {
        get
        {
            netPay = Math.Round((grossPay - ficaTax - fedTax - stateTax - healthIns), 2, MidpointRounding.AwayFromZero);
            return netPay;
        }
    }

    private double FicaTax
    {
        get
        {
            ficaTax = Math.Round((FICA_TAX * grossPay), 2, MidpointRounding.AwayFromZero);
            return ficaTax;
        }
    }

    private double FedTax
    {
        get
        {
            fedTax = Math.Round((FED_TAX * grossPay), 2, MidpointRounding.AwayFromZero);
            return fedTax;
        }
    }

    private double StateTax
    {
        get
        {
            stateTax = Math.Round((STATE_TAX * grossPay), 2, MidpointRounding.AwayFromZero);
            return stateTax;
        }
    }

    private double HealthIns
    {
        get
        {
            healthIns = Math.Round((HEALTH_INS * grossPay), 2, MidpointRounding.AwayFromZero);
            return healthIns;
        }
    }

    public override string ToString()
    {
        string stats;
        stats = string.Format("Name\t\t  {0} \n", workerName);
        stats += string.Format("Gross Pay\t  {0:c} \n", GrossPay);
        stats += string.Format("FICA tax\t  {0:c} \n", FicaTax);
        stats += string.Format("Federal tax\t  {0:c} \n", FedTax);
        stats += string.Format("State tax\t  {0:c} \n", StateTax);
        stats += string.Format("Health Insurance  {0:c} \n", HealthIns);
        stats += string.Format("Net pay\t\t  {0:c} \n", NetPay);
        return stats;
    }
}

我相信您试图描述的是使用实现的。让我们看看您的代码。您有一个带有一组属性的类
Pay
。为简化起见,我将假设只有三个属性:
WorkerName
hoursweed
RateOfPay

让我们从描述以下属性的类开始:

public class Pay
{
    public string WorkerName { get; set; }
    public double HoursWorked { get; set; }
    public double RateOfPay { get; set; }
}
我认为上面的类定义有很多地方是“错误的”,但让我们一次解决一个问题

首先,我们需要计算医疗保险成本。它取决于医疗保险乘数(0.7),在我们的例子中,它是一个静态值。让我们将其添加到一个类中:

public class Pay
{
    private static double HEALTH_INS = 0.07;

    public string WorkerName { get; set; }
    public double HoursWorked { get; set; }
    public double RateOfPay { get; set; }

    private double HealthIns
    {
        get
        {
            return Math.Round((HEALTH_INS * HoursWorked * RateOfPay), 2, MidpointRounding.AwayFromZero);
        }
    }

    // With latest versions of C# you could actually do this:
    private double HealthInsAlt =>  Math.Round((HEALTH_INS * grossPay), 2, MidpointRounding.AwayFromZero);
}
现在,让我们谈谈您的场景:您希望分配这些值。首先,如果值是相关的,并且仅在类中使用(
Pay
,在本例中)-执行我上面描述的操作完全可以,我个人更喜欢这种方式-很容易阅读代码并理解值的来源

但有时(更复杂的场景)它根本不起作用。让我们介绍一个这样的商业案例:健康保险乘数现在是动态的,取决于支付率,并且来自一个数据库。为了简化起见,我假设我们可以使用某种存储库获得它:
InsuranceMultipliersRepository.GetHealthMultiplier(RateOfPay)

我们可以检索构造函数中的值,该值在创建具有指定类的对象的新实例时调用。让我们实现它:

public class Pay
{
    private static double HEALTH_INS { get; }

    public string WorkerName { get; set; }
    public double HoursWorked { get; set; }
    public double RateOfPay { get; set; }

    private double HealthIns
    {
        get
        {
            return Math.Round((HEALTH_INS * HoursWorked * RateOfPay), 2, MidpointRounding.AwayFromZero);
        }
    }

    public Pay() {
    // This is a parameterless constructor. C# compiler actually generates this for you behind the scenes and sets every value to the default for its type. We're basically saying "use this instead of default one when creating an object" and providing additional instructions. In our case, the instruction would be retrieving the multiplier from the repository and storing it within the Pay class property.

        var insuranceRepo = new InsuranceMultipliersRepository();

        this.HEALTH_INS = insuranceRepo.GetHealthMultiplier(RateOfPay);
    }
}
现在,这里有一个严重的缺陷:我们还没有设置
RateOfPay
!这意味着我们需要修改构造函数,以确保在创建
Pay
对象的新实例时,
RateOfPay
值是已知的。让我们这样做:

public class Pay
{
    private static double HEALTH_INS { get; }

    public string WorkerName { get; set; }
    public double HoursWorked { get; set; }
    public double RateOfPay { get; set; }

    private double HealthIns
    {
        get
        {
            return Math.Round((HEALTH_INS * HoursWorked * RateOfPay), 2, MidpointRounding.AwayFromZero);
        }
    }

    public Pay(double rateOfPay) {
        var insuranceRepo = new InsuranceMultipliersRepository();
        this.RateOfPay = rateOfPay;
        this.HEALTH_INS = insuranceRepo.GetHealthMultiplier(rateOfPay);
    }
}
好的,现在看起来好多了。但是通过将构造函数更改为参数化,我们实际上使以下代码无效:
var somePay=new Pay();
.C#编译器不会为类实现生成默认的无参数构造函数(在某些情况下,例如抽象类,它仍然会这样做)

有两件事值得一提:您可以看到,对于
HEALTH\u INS
没有
set
方法,但我们仍在设置值。如何设置?现实情况是,我们可以设置
只读属性或在构造函数中没有setter的属性(使用C#6+).如果您使用的是C#5或更早的版本,您必须创建一个支持属性(对于公共属性,如果您有一个私有属性,只需将setter放在那里是安全的,没有人可以看到它),如果我们需要公开和可读我们的保险乘数,请执行以下操作:

public class Pay
{
    private static double _HEALTH_INS { get; set; }

    public double HEALTH_INS { get { return _HEALTH_INS; } }
    public string WorkerName { get; set; }
    public double HoursWorked { get; set; }
    public double RateOfPay { get; set; }

    private double HealthIns
    {
        get
        {
            return Math.Round((HEALTH_INS * HoursWorked * RateOfPay), 2, MidpointRounding.AwayFromZero);
        }
    }

    public Pay(double rateOfPay) {
        var insuranceRepo = new InsuranceMultipliersRepository();
        this.RateOfPay = rateOfPay;
        this._HEALTH_INS = insuranceRepo.GetHealthMultiplier(rateOfPay);
    }
}
关于你的代码,我注意到了另一件事:你的公共属性有setter。这有效地使你的类可变。这意味着你永远不能确定属性的值没有被更改/是正确的。想象一个场景:你创建了一个
Pay
对象的实例,并设置了
RateOfPay
。你传递了这个ob针对方法,在不同的地方使用它。然后,由于错误,您或其他开发人员编写了以下代码:

public void AdjustPayForHours (Pay payToAdjust)
{
    // If someone worked for less than half an hour their rate of pay should be reduced in half
    if (payToAdjust.HoursWorked < 0.5) {
        payToAdjust.RateOfPay = payToAdjust.RateOfPay * 5.0
    }
}
2017年3月20日更新:上述实现不会使对象深度不可变,但可以归类为浅不可变

最后,您询问了更大的应用程序。在更大的代码库中,开发人员使用不同的模式。对于我的场景(数据库中的保险乘数),有两种最有用:控制反转和依赖注入

在线上有很多关于这两种模式的文章和文档,所以我不会详细介绍。只需一个简单的解释:使用这两种模式,开发人员可以避免考虑医疗保险费率的位置、应该如何获得(控制反转),还可以避免创建新的存储库实例(或其他依赖项)当我们需要实例化一个新对象时。在我的示例中,
InsuranceMultipliersRepository
Pay
的依赖项-我们需要它来创建一个新的
Pay
。依赖项注入允许我们这样做:

public class Pay
{
    private static readonly IHealthInsuranceRepository _insuranceRepo { get; }        

    public string WorkerName { get; set; }
    public double HoursWorked { get; set; }
    public double RateOfPay { get; set; }

    private double HealthMultiplier { get { return _insuranceRepo.GetHealthMultiplier(RateOfPay); } }

    private double HealthIns
    {
        get
        {
            return Math.Round((HealthMultiplier * HoursWorked * RateOfPay), 2, MidpointRounding.AwayFromZero);
        }
    }

    public Pay(double rateOfPay, IHealthInsuranceRepository healthInsuranceRepository) {
        _insuranceRepo = healthInsuranceRepository;
        this.RateOfPay = rateOfPay;
    }
}
注意:我的代码
public class Pay
{
    private static readonly IHealthInsuranceRepository _insuranceRepo { get; }        

    public string WorkerName { get; set; }
    public double HoursWorked { get; set; }
    public double RateOfPay { get; set; }

    private double HealthMultiplier { get { return _insuranceRepo.GetHealthMultiplier(RateOfPay); } }

    private double HealthIns
    {
        get
        {
            return Math.Round((HealthMultiplier * HoursWorked * RateOfPay), 2, MidpointRounding.AwayFromZero);
        }
    }

    public Pay(double rateOfPay, IHealthInsuranceRepository healthInsuranceRepository) {
        _insuranceRepo = healthInsuranceRepository;
        this.RateOfPay = rateOfPay;
    }
}