From c536e194e75c64bfcff0cc60ad509c39a80fbe2f Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Tue, 3 Aug 2021 21:03:45 +0200 Subject: [PATCH] Add support for Events (#23) * event wip... * Add support for 'event' * event example --- .../ProxyInterfaceConsumer/Address.cs | 16 +- .../ProxyInterfaceConsumer/IPerson.cs | 2 +- src-examples/ProxyInterfaceConsumer/Person.cs | 2 +- .../ProxyInterfaceConsumer/Program.cs | 295 +++++++++--------- .../PartialInterfacesGenerator.cs | 21 +- .../FileGenerators/ProxyClassesGenerator.cs | 32 +- .../ProxyInterfaceCodeGenerator.cs | 2 +- .../Utils/MemberHelper.cs | 17 + 8 files changed, 228 insertions(+), 159 deletions(-) diff --git a/src-examples/ProxyInterfaceConsumer/Address.cs b/src-examples/ProxyInterfaceConsumer/Address.cs index 0d15551..055c152 100644 --- a/src-examples/ProxyInterfaceConsumer/Address.cs +++ b/src-examples/ProxyInterfaceConsumer/Address.cs @@ -1,7 +1,11 @@ -namespace DifferentNamespace -{ - public class Address - { - public int HouseNumber { get; set; } - } +using System; + +namespace DifferentNamespace +{ + public class Address + { + public int HouseNumber { get; set; } + + public event EventHandler MyEvent; + } } \ No newline at end of file diff --git a/src-examples/ProxyInterfaceConsumer/IPerson.cs b/src-examples/ProxyInterfaceConsumer/IPerson.cs index e565cb5..7bf496d 100644 --- a/src-examples/ProxyInterfaceConsumer/IPerson.cs +++ b/src-examples/ProxyInterfaceConsumer/IPerson.cs @@ -1,4 +1,4 @@ -namespace ProxyInterfaceConsumer +namespace ProxyInterfaceConsumer { [ProxyInterfaceGenerator.Proxy(typeof(ProxyInterfaceConsumer.Person))] public partial interface IPerson diff --git a/src-examples/ProxyInterfaceConsumer/Person.cs b/src-examples/ProxyInterfaceConsumer/Person.cs index aa7bdb2..cc0dbc2 100644 --- a/src-examples/ProxyInterfaceConsumer/Person.cs +++ b/src-examples/ProxyInterfaceConsumer/Person.cs @@ -1,6 +1,6 @@ -using DifferentNamespace; using System.Collections.Generic; using System.Threading.Tasks; +using DifferentNamespace; namespace ProxyInterfaceConsumer { diff --git a/src-examples/ProxyInterfaceConsumer/Program.cs b/src-examples/ProxyInterfaceConsumer/Program.cs index 9afbd2f..2692a00 100644 --- a/src-examples/ProxyInterfaceConsumer/Program.cs +++ b/src-examples/ProxyInterfaceConsumer/Program.cs @@ -1,146 +1,149 @@ -using AutoMapper; -using System; -using System.Collections.Generic; -using System.Text.Json; - -namespace ProxyInterfaceConsumer -{ - public class Program - { - private static JsonSerializerOptions JsonSerializerOptions = new () - { - WriteIndented = true - }; - - public static void Main() - { - IPersonT pT = new PersonTProxy(new PersonT()); - pT.TVal = 1; - Console.WriteLine(JsonSerializer.Serialize(pT, JsonSerializerOptions)); - Console.WriteLine(new string('-', 80)); - - IPersonTT pTT = new PersonTTProxy(new PersonTT()); - pTT.TVal1 = 42; - pTT.TVal2 = new Program(); - Console.WriteLine(JsonSerializer.Serialize(pTT, JsonSerializerOptions)); - Console.WriteLine(new string('-', 80)); - - IPerson p = new PersonProxy(new Person()); - p.Name = "test"; - p.HelloWorld("stef"); - Console.WriteLine("DefaultValue " + p.DefaultValue()); - Console.WriteLine("DefaultValue " + p.DefaultValue(42)); - - //var ap = new AddressProxy(new Address { HouseNumber = 42 }); - //p.Address = ap; - //var add = p.AddAddress(ap); - //Console.WriteLine("add = " + JsonSerializer.Serialize(add, JsonSerializerOptions)); - //p.AddAddress(new AddressProxy(new Address { HouseNumber = 1000 })); - - Console.WriteLine(JsonSerializer.Serialize(p, JsonSerializerOptions)); - } - } - public struct Test - { - public int Id { get; set; } - - public Clazz C { get; } - - public IList Cs { get; set; } - - public int Add(string s) - { - return 600; - } - } - - public sealed class Clazz - { - public string Name { get; set; } - } - - public interface ITest - { - int Id { get; set; } - - IClazz C { get; } - - IList Cs { get; set; } - - int Add(string s); - } - - public interface IClazz - { - string Name { get; set; } - } - - public class TestProxy : ITest - { - private Test _instance; - - private IClazz _clazz; - - private readonly IMapper _mapper; - - public TestProxy(Test instance) - { - _instance = instance; - - // _clazz = new ClazzProxy(_instance.C); - - _mapper = new MapperConfiguration(cfg => - { - //cfg.CreateMap(); - //cfg.CreateMap(); - - cfg.CreateMap(); - cfg.CreateMap(); - }).CreateMapper(); - } - - public int Id - { - get => _instance.Id; - set => _instance.Id = value; - } - - public IClazz C => _clazz; - - public IList Cs2 - { - get - { - //return null; // TinyMapper.Map>(_instance.Cs); //(IList)_instance.Cs.Select(x => new ClazzProxy(x)); - return _mapper.Map>(_instance.Cs); //(IList)_instance.Cs.Select(x => new ClazzProxy(x)); - } - - set - { - _instance.Cs = _mapper.Map>(value); - //_instance.Cs = TinyMapper.Map>(value); - } - } - - public IList Cs - { - get => _mapper.Map>(_instance.Cs); - - set => _instance.Cs = _mapper.Map>(value); - } - - public int Add(string s) => _instance.Add(s); - } - - public class ClazzProxy : IClazz - { - private Clazz _instance; - - public ClazzProxy(Clazz instance) - { - _instance = instance; - } - - public string Name { get => _instance.Name; set => _instance.Name = value; } - } -} +using AutoMapper; +using DifferentNamespace; +using System; +using System.Collections.Generic; +using System.Text.Json; + +namespace ProxyInterfaceConsumer +{ + public class Program + { + private static JsonSerializerOptions JsonSerializerOptions = new () + { + WriteIndented = true + }; + + public static void Main() + { + IPersonT pT = new PersonTProxy(new PersonT()); + pT.TVal = 1; + Console.WriteLine(JsonSerializer.Serialize(pT, JsonSerializerOptions)); + Console.WriteLine(new string('-', 80)); + + IPersonTT pTT = new PersonTTProxy(new PersonTT()); + pTT.TVal1 = 42; + pTT.TVal2 = new Program(); + Console.WriteLine(JsonSerializer.Serialize(pTT, JsonSerializerOptions)); + Console.WriteLine(new string('-', 80)); + + var ap = new AddressProxy(new Address { HouseNumber = 42 }); + ap.HouseNumber = -1; + ap.MyEvent += delegate (object x, EventArgs a) + { + }; + + IPerson p = new PersonProxy(new Person()); + p.Name = "test"; + p.HelloWorld("stef"); + // p.Address = ap; + + Console.WriteLine("DefaultValue " + p.DefaultValue()); + Console.WriteLine("DefaultValue " + p.DefaultValue(42)); + + Console.WriteLine(JsonSerializer.Serialize(p, JsonSerializerOptions)); + } + } + public struct Test + { + public int Id { get; set; } + + public Clazz C { get; } + + public IList Cs { get; set; } + + public int Add(string s) + { + return 600; + } + } + + public sealed class Clazz + { + public string Name { get; set; } + } + + public interface ITest + { + int Id { get; set; } + + IClazz C { get; } + + IList Cs { get; set; } + + int Add(string s); + } + + public interface IClazz + { + string Name { get; set; } + } + + public class TestProxy : ITest + { + private Test _instance; + + private IClazz _clazz; + + private readonly IMapper _mapper; + + public TestProxy(Test instance) + { + _instance = instance; + + // _clazz = new ClazzProxy(_instance.C); + + _mapper = new MapperConfiguration(cfg => + { + //cfg.CreateMap(); + //cfg.CreateMap(); + + cfg.CreateMap(); + cfg.CreateMap(); + }).CreateMapper(); + } + + public int Id + { + get => _instance.Id; + set => _instance.Id = value; + } + + public IClazz C => _clazz; + + public IList Cs2 + { + get + { + //return null; // TinyMapper.Map>(_instance.Cs); //(IList)_instance.Cs.Select(x => new ClazzProxy(x)); + return _mapper.Map>(_instance.Cs); //(IList)_instance.Cs.Select(x => new ClazzProxy(x)); + } + + set + { + _instance.Cs = _mapper.Map>(value); + //_instance.Cs = TinyMapper.Map>(value); + } + } + + public IList Cs + { + get => _mapper.Map>(_instance.Cs); + + set => _instance.Cs = _mapper.Map>(value); + } + + public int Add(string s) => _instance.Add(s); + } + + public class ClazzProxy : IClazz + { + private Clazz _instance; + + public ClazzProxy(Clazz instance) + { + _instance = instance; + } + + public string Name { get => _instance.Name; set => _instance.Name = value; } + } +} diff --git a/src/ProxyInterfaceSourceGenerator/FileGenerators/PartialInterfacesGenerator.cs b/src/ProxyInterfaceSourceGenerator/FileGenerators/PartialInterfacesGenerator.cs index 7845034..b63802a 100644 --- a/src/ProxyInterfaceSourceGenerator/FileGenerators/PartialInterfacesGenerator.cs +++ b/src/ProxyInterfaceSourceGenerator/FileGenerators/PartialInterfacesGenerator.cs @@ -1,10 +1,11 @@ +using System.Collections.Generic; +using System.Linq; +using System.Text; using Microsoft.CodeAnalysis; using ProxyInterfaceSourceGenerator.Enums; using ProxyInterfaceSourceGenerator.Extensions; using ProxyInterfaceSourceGenerator.SyntaxReceiver; using ProxyInterfaceSourceGenerator.Utils; -using System.Collections.Generic; -using System.Text; namespace ProxyInterfaceSourceGenerator.FileGenerators { @@ -47,6 +48,8 @@ namespace {ns} {GenerateProperties(targetClassSymbol, proxyAll)} {GenerateMethods(targetClassSymbol)} + +{GenerateEvents(targetClassSymbol)} }} }}"; @@ -89,5 +92,19 @@ namespace {ns} return str.ToString(); } + + private string GenerateEvents(INamedTypeSymbol targetClassSymbol) + { + var str = new StringBuilder(); + foreach (var @event in MemberHelper.GetPublicEvents(targetClassSymbol)) + { + var ps = @event.First().Parameters.First(); + var type = ps.GetTypeEnum() == TypeEnum.Complex ? GetParameterType(ps, out _) : ps.Type.ToString(); + str.AppendLine($" event {type} {@event.Key.GetSanitizedName()};"); + str.AppendLine(); + } + + return str.ToString(); + } } } \ No newline at end of file diff --git a/src/ProxyInterfaceSourceGenerator/FileGenerators/ProxyClassesGenerator.cs b/src/ProxyInterfaceSourceGenerator/FileGenerators/ProxyClassesGenerator.cs index fdb69ac..6e16004 100644 --- a/src/ProxyInterfaceSourceGenerator/FileGenerators/ProxyClassesGenerator.cs +++ b/src/ProxyInterfaceSourceGenerator/FileGenerators/ProxyClassesGenerator.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Text; @@ -20,7 +20,7 @@ namespace ProxyInterfaceSourceGenerator.FileGenerators { foreach (var ci in _context.CandidateInterfaces) { - yield return GenerateFile(ci.Value); //.Namespace, ci.Value.InterfaceName, ci.Value.ClassName, ci.Value.TypeName, ci.Value.ProxyAll); + yield return GenerateFile(ci.Value); } } @@ -54,6 +54,8 @@ namespace {ns} {GeneratePublicMethods(targetClassSymbol)} +{GenerateEvents(targetClassSymbol)} + public {constructorName}({targetClassSymbol} instance) {{ _Instance = instance; @@ -197,5 +199,31 @@ namespace {ns} return str.ToString(); } + + private string GenerateEvents(INamedTypeSymbol targetClassSymbol) + { + var str = new StringBuilder(); + foreach (var @event in MemberHelper.GetPublicEvents(targetClassSymbol)) + { + var name = @event.Key.GetSanitizedName(); + var ps = @event.First().Parameters.First(); + var type = ps.GetTypeEnum() == TypeEnum.Complex ? GetParameterType(ps, out _) : ps.Type.ToString(); + str.Append($" public event {type} {name} {{"); + + if (@event.Any(e => e.MethodKind == MethodKind.EventAdd)) + { + str.Append($" add {{ _Instance.{name} += value; }}"); + } + if (@event.Any(e => e.MethodKind == MethodKind.EventRemove)) + { + str.Append($" remove {{ _Instance.{name} -= value; }}"); + } + + str.AppendLine(" }"); + str.AppendLine(); + } + + return str.ToString(); + } } } \ No newline at end of file diff --git a/src/ProxyInterfaceSourceGenerator/ProxyInterfaceCodeGenerator.cs b/src/ProxyInterfaceSourceGenerator/ProxyInterfaceCodeGenerator.cs index d477217..60ad2ce 100644 --- a/src/ProxyInterfaceSourceGenerator/ProxyInterfaceCodeGenerator.cs +++ b/src/ProxyInterfaceSourceGenerator/ProxyInterfaceCodeGenerator.cs @@ -1,4 +1,4 @@ -using System.Text; +using System.Text; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Text; using ProxyInterfaceSourceGenerator.FileGenerators; diff --git a/src/ProxyInterfaceSourceGenerator/Utils/MemberHelper.cs b/src/ProxyInterfaceSourceGenerator/Utils/MemberHelper.cs index cbdf2b2..e49efec 100644 --- a/src/ProxyInterfaceSourceGenerator/Utils/MemberHelper.cs +++ b/src/ProxyInterfaceSourceGenerator/Utils/MemberHelper.cs @@ -31,6 +31,23 @@ namespace ProxyInterfaceSourceGenerator.Utils filter); } + public static IEnumerable> GetPublicEvents(INamedTypeSymbol classSymbol, Func? filter = null) + { + if (filter is null) + { + filter = _ => true; + } + +#pragma warning disable CS8619 // Nullability of reference types in value doesn't match target type. +#pragma warning disable RS1024 // Compare symbols correctly + return GetPublicMembers(classSymbol, + m => m.MethodKind == MethodKind.EventAdd || m.MethodKind == MethodKind.EventRemove/* || m.MethodKind == MethodKind.EventRaise*/, + filter) + .GroupBy(e => e.AssociatedSymbol); +#pragma warning restore RS1024 // Compare symbols correctly +#pragma warning restore CS8619 // Nullability of reference types in value doesn't match target type. + } + // TODO : do we need also to check for "SanitizedName()" here? private static IEnumerable GetPublicMembers(INamedTypeSymbol classSymbol, params Func[] filters) where T : ISymbol {