如何使基类方法上的属性应用于继承的类

本文关键字:应用于 继承 属性 类方法 何使基 | 更新日期: 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
        }
    }