Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add List Properties, Attributes #21

Open
timeshift92 opened this issue Sep 13, 2022 · 6 comments
Open

Add List Properties, Attributes #21

timeshift92 opened this issue Sep 13, 2022 · 6 comments

Comments

@timeshift92
Copy link

hello cool idea, but is it possible to add a list of properties of a class, let's say I want to create something like an interceptor

public class Interceptor<T>
{

    public void OnEntry(string Name, T Value)
    {
        System.Console.WriteLine("entry");
    }
    public void OnExit(string Name, T Value)
    {
        System.Console.WriteLine("exit");
    }

    public void OnException(string Name, T Value)
    {
        System.Console.WriteLine("Execption");
    }


    public T Intercept(string Name, T Value)
    {
        try
        {
            OnEntry(Name, Value);
            return T[Name](Name, Value);

        }
        catch (System.Exception)
        {
            OnException(Name, Value);
            throw;

        }
        finally
        {
            OnExit(Name, Value);
        }


    }
}
public abstract class GobieManipulatorGenerator{}

//[GobieGlobalChildTemplate("EFCoreRegistrationGenerator")]
public class InterceptorGenerator:GobieManipulatorGenerator {
    // [GobieTemplate]
        private const string EncapsulationTemplate = @$"
            {{ for field in fields}}
                public string {{ field.Name | regex.replace ""-"" """" | pascal  }}
                {{
                    get => {{field.Name}};
                    set
                    {{
                        var interceptor = new Interceptor<string>();
                        {{field.Name}} = interceptor.Intercept(""{{ field.Name | regex.replace ""-"" """" | pascal  }}"", value);
                    }}
                }}
            {{ end }}
            {{ for method in methods}}
             {{method.attribute}} 
            {{end}}
        
        ";
}


//[Proxied]
public partial class Author
{
    string _name;
    int _age;

    public void UpdateAuthor(string Name, int Age)
    {
        this.Name = Name;
        this.Age = Age;
    }
}

/// Generated
public partial class Author
{

    public string Name
    {
        get => _name;
        set
        {
            var interceptor = new Interceptor<string>();
            _name = interceptor.Intercept("Name", value);
        }
    }

    public int Age
    {
        get => _age;
        set
        {
            var interceptor = new Interceptor<int>();
            _age = interceptor.Intercept("Name", value);
        }
    }
}
@Siphonophora
Copy link
Collaborator

As a quick starting note, the generated code you show could be produced, assuming you were willing to have attributes on each field.

public partial class Author
{
    [InterceptedProperty] string _name;
    [InterceptedProperty] int _age;

    public void UpdateAuthor(string Name, int Age)
    {
        this.Name = Name;
        this.Age = Age;
    }
}

But, I gather that isn't what you are asking. If I'm understanding it, {{ for field in fields}} and {{ for method in methods}} are the core of your question.

I'm sure we could support this kind of looping, technically, but I suspect the complexity would sprawl. There will be a point where custom generators or post sharp are better answers. I don't know what the cutoff will be, so examples of when it is useful will help me decide that. But we can still do some of this.

{{ for field in fields}}

For this one, I have had an idea on what to do for a while.

I think what we can do is define a generator which would apply to all fields on a class which meets a particular condition: specifically that it implements a specific interface OR that it is assignable to a particular type. So we could apply a template to all IEnumerable fields, or in your example, we would probably apply to all object.

It would avoid the for looping, in the template, by doing that within Gobie itself.

The value I see here would be from only needing the one attribute on the class to ensure all the appropriate fields get code gen. So its more of a convenience to the user than a new feature.

{{ for method in methods}}

This is harder. We can discover all the methods, and all their parameters for sure. But if a user looped over all of them in a template, i'm not sure what they could do with all of them. And unlike the fields, I can't think of a way to find the right methods to work with. So I think that leaves us with applying attributes to each method directly, to indicate we want code gen.

We could take a private method, and generate a public one that would forward a list of arguments and values to an interceptor. I'm not sure what would make sense to do.

I think whether or not anything along these lines happens will depend on finding a good use case to show the value.

Regex replace

Is regex.replace ""-"" """" there to ensure that the _age gets formatted as Age without the underscore? If thats all it is I think the Pascal format probably should handle that based on convention. If you had other ideas for where pipelining like field.Name | regex.replace ""-"" """" | pascal would be needed, let me know. It may need to be its own issue.

@timeshift92
Copy link
Author

but postsharp is paid =(

@Siphonophora
Copy link
Collaborator

Yep, and IL weaving has capabilities that roslyn source gen doesn't, by design. So this can't compete directly with postsharp anyway.

The point on the limitations is that within what we can do with roslyn source gen, I want to provide as much capability as possible without letting the library become very complicated to use or maintain.

@timeshift92
Copy link
Author

Thanks, your idea is great, and plus I think I found a free injection kit
https://github.com/pamidur/aspect-injector,

the essence of my problem was that, I wanted to create proxy attributes:

using OneOf;
using Shared;


namespace Services{

public class TodoServiceProxy : TodoService
{

    public override Task<int> CreateTodo(CreateTodoCommand command)
    {
        return Call("CreateTodo", command).AsT2;
    }

    public void Complete() {  };
    public int Sum(int a, int b) { return Call("Sum", new Tuple<int, int>(a, b)).AsT0; }
    public bool IsCorrect(bool corr = true) { return Call("IsCorrect", corr).AsT1; }


    public OneOf<int, bool, Task<int>> Call(string methodName, OneOf<Tuple<int, int>, bool, CreateTodoCommand> args)
    {
        return methodName switch
        {
            "Sum" => base.Sum(args.AsT0.Item1, args.AsT0.Item1),
            "IsCorrect" => base.IsCorrect(args.AsT1),
            "CreateTodo" => base.CreateTodo(args.AsT2),
            _ => throw new NotImplementedException(),
        };
    }
}
}

as you can see in the example, I want to make something like dynamic calls without reflection, and in principle I succeed, this is where the arguments were needed, but I understand this will complicate =(

@Siphonophora
Copy link
Collaborator

I don't think i'm following this example. Is everything shown generated code?

@timeshift92
Copy link
Author

I created it myself, I just wanted to show, I will still find a use for your package =)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants