问题
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