有没有比这更好的方法来将函数注入 Unity 中的对象?
Is there a better way than this to inject a function into an object in Unity?
我是 Unity 菜鸟,我有一个问题。我想将 IAgeCalculator 注入每个 IPerson 实例,以便 IAgeCalculator 实例可供我以后创建的任何 IPerson 使用。
这是我到目前为止所尝试过的方法。能用,但感觉不对。
static void Main(string[] args)
{
IUnityContainer container = new UnityContainer();
container.RegisterType<IAgeCalculator, AgeInYearsCalculator>("Years");
container.RegisterType<IAgeCalculator, AgeInDaysCalculator>("Days");
// Using the initializer like this does not feel right,
// but I cant think of another way...
var ageCalculator = container.Resolve<IAgeCalculator>("Days");
var personInitializer = new InjectionMethod("Initializer", ageCalculator);
container.RegisterType<IPerson, Person>(personInitializer);
var person1 = Factory<IPerson>.Create(container);
person1.Name = "Jacob";
person1.Gender = "Male";
person1.Birthday = new DateTime(1995, 4, 1);
var person2 = Factory<IPerson>.Create(container);
person2.Name = "Emily";
person2.Gender = "Female";
person2.Birthday = new DateTime(1998, 10, 31);
}
这是我的 class 定义:
public interface IAgeCalculator
{
string GetAge(IPerson person);
}
internal class AgeInYearsCalculator : IAgeCalculator
{
public string GetAge(IPerson person) {
var years = DateTime.Now.Subtract(person.Birthday).TotalDays / 365;
return years.ToString("0.00") + " years";
}
}
internal class AgeInDaysCalculator : IAgeCalculator
{
public string GetAge(IPerson person) {
var days = (DateTime.Now.Date - person.Birthday).TotalDays;
return days.ToString("#,#") + " days";
}
}
public interface IPerson
{
string Name { get; set; }
string Gender { get; set; }
DateTime Birthday { get; set; }
string Age { get; }
}
public class Person : IPerson
{
private IAgeCalculator _ageCalculator;
public void Initializer(IAgeCalculator ageCalculator) {
_ageCalculator = ageCalculator;
}
public string Name { get; set; }
public string Gender { get; set; }
public DateTime Birthday { get; set; }
public string Age => _ageCalculator.GetAge(this);
}
public class Factory<T>
{
public static T Create(IUnityContainer container) {
return container.Resolve<T>();
}
}
有没有更好的方法?
DI 容器的目的是构建应用程序组件的对象图。应用程序组件通常是包含应用程序行为的长期对象。组件通常是不可变的,它们实现的服务抽象应该始终是不可变的。
DI 容器并不意味着构建短暂且可变的对象,例如 DTO 和实体(例如您的 Person
对象)。解析或注入可变的、短暂的数据对象会导致歧义。例如,如果我们注入一个Person
,我们期望得到哪个人?
这通常意味着您不应对数据对象使用构造函数注入。在您的情况下,甚至不需要 IAgeCalculator
作为对 Person
对象的依赖。 Person
的消费者很容易依赖 IAgeCalculator
并为正确的人调用 IAgeCalculator.GetAge
。
如果您想在您的实体中放置特定领域的逻辑,方法注入 非常适合。这意味着负责调用该域方法的组件负责转发所需的服务。例如:
public void Handle(AddItemToBasketCommand command)
{
var product = this.productRepository.Get(command.ProductId);
var person = this.personRepository.Get(this.userContext.UserId);
if (product.IsAdultProduct && person.GetAge(this.ageCalculator) < 18) {
throw new ValidationException("You must be 18 to order this item.");
}
var basket = this.basketService.GetUserBasket();
basket.AddItemToBasket(command.ProductId, command.Quantity);
}
请注意,我的 Person.GetAge
方法 returns 年龄。在您的情况下,您的 AgeCalculator
returns 是该人年龄的字符串表示形式。这不是属于实体本身的责任。这是一个演示责任;它会因为不同的原因而改变,而不是为什么 Person class 的其他部分会改变。这是实体内部没有此逻辑的另一个原因。
另请注意,对您的实体进行 IPerson
之类的抽象通常意义不大。实体是数据容器,而接口是用来抽象行为的。 Person
中没有您想要抽象的行为,因为您不太可能希望将来有多个 Person
的实现。
使用 RegisterInstance<T>
(不是 RegisterType<T>
)注册 IAgeCalculator
并传入从该类型派生的实例。
static void Main(string[] args)
{
IUnityContainer container = new UnityContainer();
container.RegisterInstance<IAgeCalculator>("Years", new AgeInYearsCalculator());
container.RegisterInstance<IAgeCalculator>("Days", new AgeInDaysCalculator());
container.RegisterType<IPerson, Person>();
var person1 = Factory<IPerson>.Create(container);
person1.Name = "Jacob";
person1.Gender = "Male";
person1.Birthday = new DateTime(1995, 4, 1);
var person2 = Factory<IPerson>.Create(container);
person2.Name = "Emily";
person2.Gender = "Female";
person2.Birthday = new DateTime(1998, 10, 31);
}
然后在您的 Person
class 中,用 [Dependency]
属性标记 AgeCalculator
。
public class Person : IPerson
{
[Dependency("Days")]
public IAgeCalculator AgeCalculator { get; set; }
public string Name { get; set; }
public string Gender { get; set; }
public DateTime Birthday { get; set; }
public string Age => AgeCalculator.GetAge(this);
}
我是 Unity 菜鸟,我有一个问题。我想将 IAgeCalculator 注入每个 IPerson 实例,以便 IAgeCalculator 实例可供我以后创建的任何 IPerson 使用。
这是我到目前为止所尝试过的方法。能用,但感觉不对。
static void Main(string[] args)
{
IUnityContainer container = new UnityContainer();
container.RegisterType<IAgeCalculator, AgeInYearsCalculator>("Years");
container.RegisterType<IAgeCalculator, AgeInDaysCalculator>("Days");
// Using the initializer like this does not feel right,
// but I cant think of another way...
var ageCalculator = container.Resolve<IAgeCalculator>("Days");
var personInitializer = new InjectionMethod("Initializer", ageCalculator);
container.RegisterType<IPerson, Person>(personInitializer);
var person1 = Factory<IPerson>.Create(container);
person1.Name = "Jacob";
person1.Gender = "Male";
person1.Birthday = new DateTime(1995, 4, 1);
var person2 = Factory<IPerson>.Create(container);
person2.Name = "Emily";
person2.Gender = "Female";
person2.Birthday = new DateTime(1998, 10, 31);
}
这是我的 class 定义:
public interface IAgeCalculator
{
string GetAge(IPerson person);
}
internal class AgeInYearsCalculator : IAgeCalculator
{
public string GetAge(IPerson person) {
var years = DateTime.Now.Subtract(person.Birthday).TotalDays / 365;
return years.ToString("0.00") + " years";
}
}
internal class AgeInDaysCalculator : IAgeCalculator
{
public string GetAge(IPerson person) {
var days = (DateTime.Now.Date - person.Birthday).TotalDays;
return days.ToString("#,#") + " days";
}
}
public interface IPerson
{
string Name { get; set; }
string Gender { get; set; }
DateTime Birthday { get; set; }
string Age { get; }
}
public class Person : IPerson
{
private IAgeCalculator _ageCalculator;
public void Initializer(IAgeCalculator ageCalculator) {
_ageCalculator = ageCalculator;
}
public string Name { get; set; }
public string Gender { get; set; }
public DateTime Birthday { get; set; }
public string Age => _ageCalculator.GetAge(this);
}
public class Factory<T>
{
public static T Create(IUnityContainer container) {
return container.Resolve<T>();
}
}
有没有更好的方法?
DI 容器的目的是构建应用程序组件的对象图。应用程序组件通常是包含应用程序行为的长期对象。组件通常是不可变的,它们实现的服务抽象应该始终是不可变的。
DI 容器并不意味着构建短暂且可变的对象,例如 DTO 和实体(例如您的 Person
对象)。解析或注入可变的、短暂的数据对象会导致歧义。例如,如果我们注入一个Person
,我们期望得到哪个人?
这通常意味着您不应对数据对象使用构造函数注入。在您的情况下,甚至不需要 IAgeCalculator
作为对 Person
对象的依赖。 Person
的消费者很容易依赖 IAgeCalculator
并为正确的人调用 IAgeCalculator.GetAge
。
如果您想在您的实体中放置特定领域的逻辑,方法注入 非常适合。这意味着负责调用该域方法的组件负责转发所需的服务。例如:
public void Handle(AddItemToBasketCommand command)
{
var product = this.productRepository.Get(command.ProductId);
var person = this.personRepository.Get(this.userContext.UserId);
if (product.IsAdultProduct && person.GetAge(this.ageCalculator) < 18) {
throw new ValidationException("You must be 18 to order this item.");
}
var basket = this.basketService.GetUserBasket();
basket.AddItemToBasket(command.ProductId, command.Quantity);
}
请注意,我的 Person.GetAge
方法 returns 年龄。在您的情况下,您的 AgeCalculator
returns 是该人年龄的字符串表示形式。这不是属于实体本身的责任。这是一个演示责任;它会因为不同的原因而改变,而不是为什么 Person class 的其他部分会改变。这是实体内部没有此逻辑的另一个原因。
另请注意,对您的实体进行 IPerson
之类的抽象通常意义不大。实体是数据容器,而接口是用来抽象行为的。 Person
中没有您想要抽象的行为,因为您不太可能希望将来有多个 Person
的实现。
使用 RegisterInstance<T>
(不是 RegisterType<T>
)注册 IAgeCalculator
并传入从该类型派生的实例。
static void Main(string[] args)
{
IUnityContainer container = new UnityContainer();
container.RegisterInstance<IAgeCalculator>("Years", new AgeInYearsCalculator());
container.RegisterInstance<IAgeCalculator>("Days", new AgeInDaysCalculator());
container.RegisterType<IPerson, Person>();
var person1 = Factory<IPerson>.Create(container);
person1.Name = "Jacob";
person1.Gender = "Male";
person1.Birthday = new DateTime(1995, 4, 1);
var person2 = Factory<IPerson>.Create(container);
person2.Name = "Emily";
person2.Gender = "Female";
person2.Birthday = new DateTime(1998, 10, 31);
}
然后在您的 Person
class 中,用 [Dependency]
属性标记 AgeCalculator
。
public class Person : IPerson
{
[Dependency("Days")]
public IAgeCalculator AgeCalculator { get; set; }
public string Name { get; set; }
public string Gender { get; set; }
public DateTime Birthday { get; set; }
public string Age => AgeCalculator.GetAge(this);
}