构造函数不能调用自身。

3
我使用Castle Windsor作为依赖注入机制,但我遇到了问题。我使用构造函数注入类型,这个方法运行良好。
但是,当我声明其他类的构造函数时,我需要调用默认构造函数,这时依赖注入魔法就会发生。
因此,我有以下代码:
private readonly IUserService UserService = null;

public CustomAccessAttribute(IUserService userService)
{
    this.UserService = userService;
}

public CustomAccessAttribute(bool someParam) : this() //here I'd like to call the above constructor
{
    ....           
}

但是我遇到了错误:

构造函数 'CustomAccessAttribute.CustomAccessAttribute(bool)' 不能调用自身

我不想在 this() 调用中手动放置 userService 对象,因为依赖注入容器应该负责这个。那么,我该如何修复此错误?


你的无参构造函数在哪里? - Nathan Cooper
你有默认构造函数吗?否则,this() 将无法工作。 - Yuval Itzchakov
1
调用默认构造函数,这是 DI 魔法发生的地方。这是什么意思?DI 魔法不可能在默认构造函数中发生。 - Callum Bradbury
2
当我声明其他类的构造函数时,我需要调用默认构造函数,这是依赖注入魔法发生的地方。这没有任何意义。您不需要自己调用任何默认构造函数。请澄清您的意思。 - Yuval Itzchakov
1
默认构造函数在构造函数注入的定义下不会发生任何魔法...我认为你混淆了这个术语。需要更多的代码才能确定具体情况。 - Jonas Høgh
你选择的构造函数重载是由调用签名决定的。一旦代码被编译,依赖注入(DI)无法改变你的调用解析到哪个构造函数。如果你想调用一个接受 IUserService 的构造函数,你需要提供一个实例,或让 DI 这样做。一旦已经调用了构造函数,DI 就无法帮助你处理其他构造函数的问题了。 - Asad Saeeduddin
2个回答

7

我认为你的方法不太对。

如果你希望DI容器选择正确的重载函数,你必须提供所有想要接收的参数。仅接受一个bool是不够的,因为你实际上还需要IUserService接口。它不会自己出现。你的DI需要知道它的存在,并将其传递给适当的构造函数。

你需要的是:

private readonly IUserService UserService = null;
public CustomAccessAttribute(IUserService userService)
{
    this.UserService = userService;
}

public CustomAccessAttribute(IUserService userService, bool someParam) : this(userService) 
{         
}

通常情况下,你希望将构造函数的链接从最少的参数到最多的参数进行。因此,你可能需要这样做:

private readonly IUserService UserService = null;
public CustomAccessAttribute(IUserService userService) : this(userService, false)
{
}

public CustomAccessAttribute(IUserService userService, bool someParam)
{  
    this.UserService = userService;
}

编辑:

如果你想传递给构造函数的bool在注入时不可用,那么构造函数注入可能根本不是正确的方法。也许更适合的方法是将其设置为属性,当bool可用时可以设置它,或者使用工厂模式,在一切都准备好时创建类实例。


1
我认为这对OP的情况不起作用。看起来DI无法为“someParam”提供合理的值。 - Asad Saeeduddin
@AsadSaeeduddin 我认为没有什么问题。他只需要在类注入时配置传递 bool 参数即可。如果此时 bool 不可用,则可能需要考虑其他接受方式,而不是使用类构造函数。 - Yuval Itzchakov
@AsadSaeeduddin 我同意 bool 是通常会在运行时变化的东西。但由于 OP 没有指定它是什么,而且它甚至可能不是一个 bool(也许 OP 只是创建了一个简单的例子),我不知道。OP 应该澄清。 - Yuval Itzchakov
1
OP不应该在属性上使用DI,如果他必须在解析时指定数据,最好的选择是向容器注册一个工厂,然后将其注入给指定布尔值的人。 - Callum Bradbury
是的,我希望它不是一个属性。从技术上讲,可以进行依赖注入,但在那个点上,这是真正的巫术,我不相信 OP 有能力做到。我认为他问题的正确答案是工厂模式,没有其他方法(我所知道的)可以可靠地为每个新实例指定不同的 someParam,而不会引起严重的头痛。 - Callum Bradbury
显示剩余4条评论

3
您似乎对IoC的工作原理感到困惑。当您想创建一个类的实例时,并没有什么“神奇”的事情发生来传递参数,如果您手动new一个CustomAccessAttribute的实例,Windsor将无法处理它,因为它不知道您正在创建一个实例。Windsor(通常)遵循以下步骤:
  1. 向容器注册IUserService的实现
  2. 调用myContainer.Resolve<CustomAccessAttribute>()(或解决具有CustomAccessAttribute依赖项的东西)
  3. 容器查看CustomAccessAttribute并发现构造函数需要一个IUserService
  4. 容器构建一个IUserService的实现并将其传递到构造函数中,然后将新的CustomAccessAttribute返回给您(或需要注入它的人)
这些步骤都不需要任何“神奇”操作,但它们都需要您与容器本身进行交互,而不仅仅是new实例并依赖于DI Magic。
如果您创建了一个默认构造函数而没有传递IUserService,那么如果您使用Resolve(),则会发现IUserService也会被解析,因为Windsor总是解析它已经注册了实现的最多参数的构造函数。
真正的问题是为什么属性首先需要一个IUserService,因为它们只应该用于元数据,而不是行为。我甚至认为,如果没有大量额外的设置,Windsor也不会愉快地解析属性,这暗示了它是多么糟糕的想法。

这可能是由于 Asp.Net Mvc 的可怕设计,它鼓励在属性中编写行为。 - Jonas Høgh

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接