是否可以在构建期间通过 PostSharp 访问引入/编织的接口和成员? [英] Is It Possible to Access Introduced/Weaved Interfaces and Members by PostSharp During Build Time?

查看:26
本文介绍了是否可以在构建期间通过 PostSharp 访问引入/编织的接口和成员?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在设计一个场景,其中两个 PostSharp 方面相互配合.我有一个方面(下面代码中的FirstAspect)旨在引入一个接口,然后另一个方面(下面代码中的SecondAspect)应该与第一个方面引入的接口.

I am designing a scenario where two PostSharp aspects are working with each other. I have one aspect (FirstAspect in the code below) that is meant to introduce an interface, and then another aspect (SecondAspect in the code below) is supposed to work with the interface that was introduced by the first aspect.

但是,第一个方面引入的接口似乎永远无法用于第二个方面.

However, it does not seem that the interface that is introduced by the first aspect is ever available to the second aspect.

这是我目前正在使用的代码:

Here is the code that I am currently working with:

public class Tests
{
    [Fact]
    public void Verify()
    {
        // Not really all that significant as the current code does not compile correctly:
        var sut = new MyClass();
        Assert.True( sut is IInterface );
    }

    public interface IInterface
    {
        void HelloWorld();
    }

    [IntroduceInterface( typeof(IInterface) )]
    public class FirstAspect : InstanceLevelAspect, IInterface, IAspectProvider
    {
        public void HelloWorld() {}

        public IEnumerable<AspectInstance> ProvideAspects( object targetElement )
        {
            // Implementing IAspectProvider appears to ensure this aspect is processed first.
            // This may be a bug.
            // Please see: http://support.sharpcrafters.com/discussions/problems/3365-runtimeinitialize-does-not-follow-ordering-rules#comment_40824072
            // for more information.
            yield break;
        }
    }

    [AspectTypeDependency( AspectDependencyAction.Order, AspectDependencyPosition.After, typeof(FirstAspect) )]
    public class SecondAspect : InstanceLevelAspect, IAspectProvider
    {
        public IEnumerable<AspectInstance> ProvideAspects( object targetElement )
        {
            var type = (Type)targetElement;
            if ( !typeof(IInterface).GetTypeInfo().IsAssignableFrom( type ) )
            {
                // This is currently being thrown, as MyClass does not implement 
                // IInterface when the AppDomain is first loaded and initialized:
                throw new InvalidOperationException( $"Does not implement {typeof(IInterface)}" );
            }

            // How to access the weaved elements from FirstAspect? ...

            yield break;
        }
    }

    [FirstAspect, SecondAspect]
    class MyClass {}
}

当我构建时,SecondAspect.ProvideAspects 中的 InvalidOperationException 被抛出,因为 FirstAspect 引入的接口不可用SecondAspect 在调用时.也就是说,即使接口已被编入 MyClass 类型,加载时在当前 AppDomain 中的类型并未标记为已实现接口.

When I build, the InvalidOperationException in the SecondAspect.ProvideAspects is thrown, as the interface that was introduced by FirstAspect is not available to SecondAspect at the time the call is made. That is, even though the interface has been weaved into the MyClass type, the type as it stands within the current AppDomain as loaded is not marked as having the interface implemented.

我正在寻找的是在构建期间访问和定位目标元素上所有已知和编织的接口和成员的能力.

What I am looking for is the ability to access and locate all known and weaved interfaces and members on a target element during build time.

我查看了 ReflectionSearch,这与我的情况很接近寻找,但它似乎没有考虑在调用此 API 时编织的元素.例如,调用 ReflectionSearch.GetMembersOfType 不会在 MyClass(由 FirstAspect 引入)上产生预期的 IInterface.HelloWorld 在上面的例子中).

I looked into ReflectionSearch, and this is close to what I am looking for, but it does not appear to account for weaved elements at the time calls into this API are made. For instance, making a call to ReflectionSearch.GetMembersOfType does not yield the expected IInterface.HelloWorld on MyClass (which is introduced by FirstAspect in the example above).

在构建期间,我应该使用另一个 API 来访问 PostSharp 引入/编织的元素吗?这甚至可能吗?

Is there another API I should be using to access introduced/weaved elements by PostSharp during build-time? Is this even possible?

推荐答案

所以这个问题看起来有点老了,但我有一个类似的问题,我仍然需要一个答案(即:如何将属性引入到引入方法而不将属性应用于实现并复制它).也就是说,我可能不得不问我自己的问题,因为您所询问的模式有一些常见的步骤,这些步骤可能会解决您的困境,但不能解决我的困境.看起来您已经尝试了其中的一些,但为了其他人,我会详细说明.

So this question looks a little old, but I have a similar issue which I still need an answer to (which is: how do I introduce an attribute to an introduced method without applying the attribute to the implementation and copying it). That said, I may have to ask my own question, as there are some common steps for the pattern you're asking about which may solve your dilemma, but do not solve mine. It looks like you've already experimented with some of this, but for the sake of others that come along, I'll detail it out.

简而言之,不要使用反射"类型来指定方面依赖.PostSharp 提供了可用于要求应用方面或要求特定顺序的属性(请参阅:应对多个方面Aspects on the Same Target 概述),以及其他方面已经提供的导入成员的方法(这个 StackOverflow答案,虽然没有标记,但是是对该用户问题的正确答案,并且还展示了将 ImportMemberAttribute 与方面依赖一起使用的方法).ImportMemberAttribute 可以导入其他方面的成员,只要顺序正确;如果该成员不存在且不是由方面引入的,则此属性的 IsRequired 属性将导致构建错误.

In short, don't use "reflection" types to specify aspect dependency. PostSharp provides attributes which you can use to require aspects to be applied or to require specific order (See: Coping with Several Aspects on the Same Target for an overview), as well as a method for importing members already provided by other aspects (This StackOverflow Answer, while not marked, is the correct answer to that user's question and also shows a way to use the ImportMemberAttribute together with aspect dependency). The ImportMemberAttribute is capable of importing members from other aspects as long as the order is correct; the IsRequired property on this attribute will cause a build error if the member does not exist and was not introduced by an aspect.

现在,如果您希望第二个方面能够应用于实现接口的所有类,无论该接口是否由您的第一个方面应用,您都可以将 AspectTypeDependencyAttribute 设置为 AspectDependencyAction.Order,但未使用 AspectDependencyAction.Required 设置 AspectTypeDependencyAttribute;如果您只想将第二个方面应用于第一个建议的目标,那么您可以应用多个依赖属性来指定要求和顺序,并且不需要 Aspect Provider(上面的答案还显示了将 Advice 应用于多个的替代实现单个方面中的切入点).类似地,如果您希望您的第一个方面始终需要您的第二个方面,您可以应用额外的 AspectTypeDependencyAttribute 来指定另一个方向的需求(即,如果两者都需要另一个,您希望在两个方面都指定需求).

Now, if you want your second aspect to be able to apply to all classes that implement an interface, whether the interface was applied by your first aspect or not, you would set the AspectTypeDependencyAttribute with AspectDependencyAction.Order, but not set an AspectTypeDependencyAttribute with AspectDependencyAction.Required; if you only want the second aspect to apply to targets advised by the first then you can apply multiple dependency attributes to specify both the requirement and the order and no Aspect Provider would be required (the above answer also shows an alternative implementation applying Advice to multiple Pointcuts in a single Aspect). Similarly, if you want your first aspect to always require your second aspect you can apply an additional AspectTypeDependencyAttribute to specify requirement in the other direction (i.e. if both require the other you want the requirement specified on both).

方面优先级"也可用于确定应用方面的顺序,尽管您应该尽可能使用依赖项,因为它们也用作合同文档.

Aspect "Priority" can also be used to determine the order aspects are applied, although whenever possible you should use the dependencies instead because they also server as contract documentation.

这一切都假设您实际上并不需要使用 Aspect Provider(因为您的评论暗示它是为订购而完成的).您不希望一个方面提供者依赖于另一个方面提供者的结果(这会违反关注点分离),相反,您需要一个方面提供者yield 为每个目标提供多个方面.但是,您也可以在 Aspect Providers 上使用 AspectTypeDependencyAttribute ,例如,您可以拥有一个 Type Level Aspect Provider,它在引入接口的类型级别方面之后进行排序,然后在提供程序中您可以循环遍历类型上的方法并注入依赖于第一个方面的方面(例如,提供者的接口引入跟随者将方法拦截建议应用于现在可以调用第一个方面引入的方法的成员).

This is all assuming you don't actually need to use an Aspect Provider (since your comments imply it was done for ordering). You would not want one Aspect Provider to depend on the results of another Aspect Provider (this would violate separation of concerns), you would instead have a single Aspect Provider yield multiple aspects for each target. You can, however, use AspectTypeDependencyAttribute on Aspect Providers as well so, for instance, you can have a Type Level Aspect Provider that orders after a type level aspect that introduces an interface and then in the provider you can loop through methods on the type and inject aspects that depend on the first aspect (e.g. an interface introduction follower by a provider that applies method interception advice to members that can now call methods introduced by the first aspect).

希望能为您解决问题(或者,考虑到自提出问题以来的时间,遇到此问题的任何其他人).其中一些信息也可能已经过时或不准确(据我所知,现在可以在某些或任何条件下检测传递给方面提供者的目标类型上的注入接口),但我相信所表达的模式仍然是PostSharp 提出的首选做法.

Hope that clears things up for you (or, given the time since the question was asked, anyone else that runs into this issue). Some of this information may also be outdated or inaccurate (for all I know, it may now be possible to detect injected interfaces on the target types passed to aspect providers, under some or any condition), but I believe the patterns expressed are still the preferred practice proposed by PostSharp.

这篇关于是否可以在构建期间通过 PostSharp 访问引入/编织的接口和成员?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆