如何使基类方法上的属性应用于继承的类
本文关键字:应用于 继承 属性 类方法 何使基 | 更新日期: 2023-09-27 18:03:48
注意:此问题已更新为新信息。请参阅本文的下半部分。(原始问题留在这里作为上下文。
<小时 />有什么方法可以定义我的属性,以便在覆盖的方法上定义它时,仍然应用该属性?
我问的原因是我有一个属性,它将一些行为注入到方法中,但是当调用该方法时,该行为不会像子类中的任何情况一样
应用,我希望它是。class BaseClass
{
[MyThing]
virtual void SomeMethod()
{
// Do something fancy because of the attribute.
}
}
class ChildClass
{
override void SomeMethod()
{
// Fancy stuff does happen here too...
base.SomeMethod();
}
void AnotherMethod()
{
// ...but not here. And I'd like it to =(
base.SomeMethod();
}
}
该属性定义如下:
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public class MyThingAttribute : Attribute
用于查找具有该属性的方法的当前代码如下:
var implementation = typeof(TheTypeWereCurrentlyInvestigating);
var allMethods = (from m in implementation.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy)
let attribs = (TransactionAttribute[]) m.GetCustomAttributes(typeof (TransactionAttribute), true)
where attribs.Length > 0
select Tuple.Create(m, attribs.Length > 0 ? attribs[0] : null)).ToList();
那部分不是我写的,我不能说我是它每个部分所做的 100%......但就目前而言,我们可以假设我控制了所有相关的代码。(这是一个开源项目,所以我至少可以创建自己的版本,并向项目所有者提交补丁......
我还有其他一些情况 - 基本上,每当我在基类上调用该方法时,无论我以哪种方式到达那里,我都希望注入这种行为 - 但是如果我解决了这个问题,我可能会得到有关如何让其他人工作的想法。如果没有,我会和他们一起回去。
<小时 />更新:
好的,所以我坐下来研究Castle.Transactions项目,并创建了一些非常简单的测试,看看哪些有效,哪些无效。事实证明,我最初对什么有效,什么无效的假设有些偏差。
我做了什么:
我创建了一个测试类,它有一个方法,用属性装饰,并调用一个Assert
方法来验证行为是否正确注入(即存在事务(。然后,我创建了几个继承此测试类的类,以查看在哪些情况下一切按我预期工作。
我发现:
通过直接在测试类上调用测试方法,并从子类上的各种方法调用测试方法,我发现了以下关于哪些有效和哪些无效的信息:
名为访问修饰符的方法有效吗?************* **************** *************基类上的 SomeMethod(( * 不适用 是其他方法(( 对孩子既不<- 头痛!OtherMethod(( on Child Hidden (new( NoSomeMethod(( on Child Hidden (new( No子项上的 OtherMethod(( 覆盖 No子项* 上的 OtherMethod(( 覆盖 Yes子项上的某个方法((覆盖是
在所有情况下,除了标有*
的那个,base.SomeMethod()
都是从测试中应用的方法调用的。在第一种情况下,调用了相同的方法,但直接来自测试,因为不涉及子类。在第二种情况下(标记为 *
的那些(,调用了覆盖方法,即 this.SomeMethod()
,所以这实际上相当于最后一种情况。我没有使用任何多余的限定符,所以在该方法中调用只是SomeMethod()
.
我想要的:
我真正想要解决的是标记为"头痛"的情况;如何将行为注入基类,即使我从我的子类调用它。
我需要该特定案例工作的原因是我在存储库中使用此模式,其中基类定义了一个用 Transaction
属性修饰的Save(T entity)
方法。目前,我必须重写此方法才能获取事务编排,这使得无法更改返回类型;在基类上它是void
,但在我的实现中,我想让它Error<T>
。这在覆盖时是不可能的,而且由于我无法通过以不同的方式命名方法来解决问题,所以我不知所措。
是你,我会试着改变你的设计。调用基地。OtherMethod(( 中的 SomeMethod(( 在 OtherMethod 的类中被覆盖时,它真的闻起来很臭。
假设 ChildClass.SomeMethod(( 仍然会调用它覆盖的方法,您不能在受保护的方法中分解出 BaseClass.SomeMethod(( 的相关部分,将您的属性放在这个新方法上并在 BaseClass.SomeMethod(( 和 OtherMethod(( 中调用它吗?
无法超越它。不能下它。必须绕过它。
考虑到我所理解的情况:
-
无法应用现有属性来注入提交/回滚:它永远不会回滚,因为您自己在
AnotherMethod()
中捕获异常。 - 您需要在
AnotherMethod()
中提交/回滚注入。
我怀疑TransactionAttribute
正在将方法的主体包装在try-catch
块中,转换这个(伪代码(:
public void SomeMethod() {
DoStuff();
}
变成这样的东西(伪代码,并且非常简化(:
public void SomeMethod() {
transaction.Begin();
try {
DoStuff();
transaction.Commit();
}
catch {
transaction.Rollback();
}
}
考虑到这一点,您可以将TransactionAttribute
应用于AnotherMethod()
并重新引发您捕获的异常:
[TransactionAttribute]
public void AnotherMethod() {
try {
DoStuff();
}
catch (Exception ex) {
//deal with exception
throw;
}
}
如果这不可行 - 例如,如果您只想要TransactionAttribute
注入的部分行为 - 那么您可能必须创建一个新TransactionAttribute
来注入您希望它注入的行为。一种可能性可能是它查找try-catch
块并将提交和回滚放在适当的位置,但这可能比当前版本更棘手。
在黑暗中拍摄,但是...
我假设事务行为是由 IoC 容器注入的,并且它通过在解析ChildClass
时创建代理来做到这一点。因此,事务代码通过代理在ChildClass.SomeMethod
之前''之后运行。我猜你看到的行为是BaseClass.SomeMethod
上没有代码注入,所以从ChildClass.AnotherMethod
调用它不涉及任何代理代码注入,它只是直接通过。
如果是这种情况,您可以使用组合模式和注入BaseClass
来解决问题。
如果您通过容器解析了以下类,它将注入一个代理BaseClass
该代理具有BaseClass.SomeMethod
方法的适当的 before''after 事务代码。因此,您将获得事务行为,以及优雅的异常处理。
您可以使用通常的OO机制来解决使AnotherChildClass
可互换BaseClass
的问题,或使用接口等。
public class AnotherChildClass
{
private readonly BaseClass _bling;
public AnotherChildClass(BaseClass bling)
{
_bling = bling;
}
public void AnotherMethod()
{
try
{
_bling.SomeMethod();
}
catch (Exception)
{
//Do nothing...
}
}
}
例如,有点急促,但你得到了图片:
public class AnotherChildClass : BaseClass
{
private readonly BaseClass _bling;
public AnotherChildClass(BaseClass bling)
{
_bling = bling;
}
public override void SomeMethod()
{
_bling.SomeMethod();
}
public void AnotherMethod()
{
try
{
_bling.SomeMethod();
}
catch (Exception)
{
//Do nothing...
}
}
}
更新
我猜从您最新的调查来看,您使用"new"的情况不起作用,因为您现在阻止生成的 IoC 容器代理覆盖 SomeMethod,从而注入代码。尝试创建 Child 类的派生类,并尝试重写 new
SomeMethod 方法。这说明了如何阻止代理。
private class BaseClass
{
public virtual void SomeMethod(){}
}
private class ChildClass : BaseClass
{
public new void SomeMethod() //<- Declaring new method will block proxy
{
base.SomeMethod();
}
}
private class ChildClassIocProxy : ChildClass
{
public override void SomeMethod() //<-- Not possible!
{
//Injected - before Tx
base.SomeMethod();
//Injected - after Tx
}
}