C# constructors having both shared and different code

筅森魡賤 提交于 2019-12-11 19:31:55

问题


Is there a way I can construct the following class in such a way that I can code the setting of Service and Values only once:

public class MyClass
{
    private readonly IService Service;
    public List<int> Values { get; set; }
    public int Value { get; set; }

    public MyClass(IService service, List<int> values)
    {
        Service = service;
        Values = values;
        Value = Values.FirstOrDefault(i => i == Service.GetDefaultValue());
    }

    public MyClass(IService service, List<int> values, int value)
    {
        Service = service;
        Values = values;
        Value = Values.FirstOrDefault(i => i == value);
    }
}

回答1:


You can use constructor chaining:

public class MyClass
{
    public MyClass(IService service, IEnumerable<int> values) 
      : this(service, values, valus.FirstOrDefault(i => i == service.GetDefaultValue()) {}

    public MyClass(IService service, IEnumerable<int> values, int value)
    {
        Service = service;
        Values = values;
        Value = value;
    }
}

Note that I've specified the values argument as an IEnumerable instead of a List. This allows the ctor to accept more types than only lists. If your member is a List type, you'll have to call the ToList() method in your constructor. However, I'd also advise you to specify the Values property as an IEnumerable instead of as a List.

Edit: after the code in the question has been changed, another advise could be to use a private constructor and (overloaded) static factory methods. Doing so indicates (imho) that the creation of such an instance is a more 'expensive' operation instead of calling a simple constructor, as you're calling a method on a 'DataService', which suggests that you might go to the DB to initialize your object ?

public class NewExpenseViewModel
{
    private readonly IDataService DataService;

    public ExpenseType ExpenseType { get; set; }

    CollectionViewSource VatRatesSource { get; set; }
    public ICollectionView VatRatesView => VatRatesSource.View;

    private NewExpenseViewModel(ServiceProvider serviceProvider, ExpenseType expenseType, VatRate vatRate)
    {
        DataService = serviceProvider.GetService<IDataService>();

        ExpenseType = expenseType;

        VatRatesSource = new CollectionViewSource() { Source = DataService.GetVatRates() };

        VatRate = vatRate;
    }

    public static NewExpenseViewModel Create(ServiceProvider sp, ExpenseType expenseType, VatRate vat)
    {
        DataService = serviceProvider.GetService<IDataService>();

        ExpenseType = expenseType;

        VatRatesSource = new CollectionViewSource() { Source = DataService.GetVatRates() };

        VatRate = vatRate;
    }


    public static NewExpenseViewModel Create(ServiceProvider sp, ExpenseType expenseType)
    {
        var instance = Create(sp, expenseType, 0);
        instance.Vat = 
            ((IEnumerable<VatRate>)VatRatesSource.Source).FirstOrDefault(v => v.VatRateID.Equals(ExpenseType.SuggestedVatRateID));

        return instance;
    }
}



回答2:


The solution I arrived at following Frederik's strategy, after realising I only needed an int as the third parameter:

public NewExpenseViewModel(ServiceProvider serviceProvider, ExpenseType expenseType, int vatRateID)
{
    DataService = serviceProvider.GetService<IDataService>();

    ExpenseType = expenseType;

    VatRatesSource = new CollectionViewSource() { Source = DataService.GetVatRates() };

    VatRate = VatRates.FirstOrDefault(v => v.VatRateID.Equals(vatRateID));
}

public NewExpenseViewModel(ServiceProvider serviceProvider, ExpenseType expenseType)
    : this(serviceProvider, expenseType, expenseType.SuggestedVatRateID) { }


来源:https://stackoverflow.com/questions/58923510/c-sharp-constructors-having-both-shared-and-different-code

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!