GithubHelp home page GithubHelp logo

someta's Introduction

Build Release

Introduction

What is this library? It aims to be a swiss-army knife toolkit for easy meta programming in C# built on the back of the Fody engine. It offers the ability to intercept methods, properties, and events; inject fields into classes; and inject code inside constructors.

Installation

Install-Package Someta.Fody

Summary

It provides a number of extension points for you to customize your compiled code. The current set of available extension points are:

  • Property interceptors
    • get in the form of IPropertyGetInterceptor
    • set in the form of IPropertySetInterceptor
  • Event interceptors
    • add in the form of IEventAddInterceptor
    • remove in the form of IEventRemoveInterceptor
  • Method interceptors
    • IMethodInterceptor will intercept all methods unless the extension also implements IAsyncMethodInterceptor, in which case async methods (defined as returning an instance of Task or Task<T>) are ignored and handled by the following extension point.
    • IAsyncMethodInterceptor will intercept only methods that return Task or Task<T> and allows the interceptor to use async semantics (i.e. await) when intercepting.
  • State
    • IStateExtensionPoint allows you to inject fields into the host so your extension can track state against your types and instances directly.
  • Non public access
    • INonPublicAccess allows you to inject access to non-public members of the annotated class. Used with InjectAccessAttribute InjectTargetAttribute.
  • Instance initialization
    • IInstanceInitializer gives you a place to add behavior to the end of the constructor of the annotated class. Useful for instantiating fields when using the state extension point.
    • IInstancePreinitializer gives you a place to add behavior to the start of the constructor of the annotated class. Useful for instantiating fields when using the state extension point, and you want them initialized before the actual constructor runs. Note that preinitialization begins after calling the base class constructor.

Extensions

To use these extension points, you simply subclass Attribute, implement one or more of the above interfaces, and decorate your type or members depending on your scenario. See our various samples if you want to learn by example.

Chaining

Particularly with the interceptor form of extension points, it should be noted that it's perfectly acceptable to chain multiple extensions on a single member. For example, with an IPropertyGetInterceptor, you can declare multiple extensions (i.e. [ExtensionA, ExtensionB]) and both will be applied to the property -- in that order. You can see a demonstration of this in our unit tests.

Scopes

Some extension point types allow you to specify the scope on which the extension should apply. For example, IStateExtensionPoint has a generic version that takes as its type argument one of the types defined in ExtensionPointScopes. By default, if your extension point is applied to a class, there will only be one instance of your extension and the only context you have is the type of the containing class. However, often you'll want to apply a single extension to your class but want the equivalent of having applied the extension to each (for example) property in that class. That's where these scopes come into play.

To make this example a bit more clear, imagine you defined an extension point that implements IStateExtensionPoint<T> and you apply it to your class, but you want to actually add state for each property in the class. To do that, you would simply implement IStateExtensionPoint<ExtensionPointScopes.Property> and Someta will understand that to mean that instead of applying the extension to the class, it will instead apply it to each property in the class.

One final question one might ask is why these scoped versions of the extension point interfaces aren't available for all the extension types. The reason is that some of the extension types can only work for a particular scope. For example, property interceptors can clearly only work on properties, so if a property interceptor extension is applied to a class, it operates implicitly as though the scope was set to Property.

FAQ

Please see our FAQ to see if it addresses any of your questions or concerns.

someta's People

Contributors

kswoll avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar

Forkers

mmintoff

someta's Issues

Target netstandard2.0, netstandard2.1 ?

I would absolutely love to use this library but the only thing stopping me is that it is unable to target older projects, which unfortunately I still need to support. I can get by using MethodBoundryAspect but this library is a bit more elegant.

Allow for attribute chaining

It would be nice if we supported the ability, for example, to allow multiple interceptors to work against the same property. Something like:

[InterceptorA, InterceptorB]
public string Foo { get; set; }

The order would be important. First InterceptorA is invoked. If it calls into proceed, then InterceptorB is invoked. (if it calls proceed, then the original implementation is invoked -- in this case, the current value of the property.

Fody Can't Find Framework

The details of this issue are here. However, I've replicated the relevant section below for posterity.


So far, I've got this attribute:

[AttributeUsage(AttributeTargets.Method)]
public class ValidatedMethodAttribute: Attribute, IMethodInterceptor
{
	public object Invoke(MethodInfo methodInfo, object instance, Type[] typeArguments, object[] arguments, Func<object[], object> invoker)
	{
		Attribute[]? attributes = methodInfo.GetCustomAttributes().Where(att => att.GetType().IsAssignableTo(typeof(IParameterValidator<>))).ToArray();
		// Do my thing
		return invoker.Invoke(arguments);
	}
}

However, when I execute the build, something weird is going on with Fody but I can't quite work out what this issue is..?

  Fody: An unhandled exception occurred:
Exception:
Failed to execute weaver /home/james/.nuget/packages/someta.fody/1.2.1/build/../weaver/Someta.Fody.dll
Type:
System.Exception
StackTrace:
   at InnerWeaver.ExecuteWeavers() in C:\projects\fody\FodyIsolated\InnerWeaver.cs:line 222
   at InnerWeaver.Execute() in C:\projects\fody\FodyIsolated\InnerWeaver.cs:line 112
Source:
FodyIsolated
TargetSite:
Void ExecuteWeavers()
System.Type was somehow not found.  Aborting.
Type:
System.InvalidOperationException
StackTrace:
   at Someta.Fody.CecilExtensions.Initialize(ModuleDefinition moduleDefinition, TypeSystem typeSystem, AssemblyNameReference soMeta)
   at Someta.Fody.ModuleWeaver.Execute()
   at InnerWeaver.ExecuteWeavers() in C:\projects\fody\FodyIsolated\InnerWeaver.cs:line 186
Source:
Someta.Fody
TargetSite:
Boolean Initialize(Mono.Cecil.ModuleDefinition, Fody.TypeSystem, Mono.Cecil.AssemblyNameReference)

As requested on our SO chat, here's the project I'm having issues with:

https://github.com/hughesjs/warm-up-exercises/tree/user-validator/05-user-validator/cs

TypeLoadException when using class level type constraints

    public class Tester<T>
        where T : class, new()
    {
        [MyAttribute]
        public void Test()
        {
            Console.WriteLine("abc");
        }
    }

Calling

var t = new Tester<SomeClass>();
t.Test();

Gives the exception:

System.TypeLoadException: 'GenericArguments[0], 'T', on 'Tester.Tester`1[T]' violates the constraint of type parameter 'T'.'

Issue only happens with class/new() constraints and only at class level.
Method level constraints of this nature work fine.
Interface constraints at class level work fine.

Add ability for interceptors to inject custom state

Suppose you want your interceptor to be able to add a custom field to the enclosing class. We should add a way for the interceptor to specify a System.Type via an attribute property enforced via an interface (such as IHasStateInterceptor with property Type StateType). This state should then be made available when the interception is happening. (Perhaps we should introduce a new "context" class that optionally includes that state)

Ultimately, this should enable implementing [ObservableAsProperty] from ReactiveUI.Fody without a separate plugin.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.