From 889b830d7e5903fe54d7dc0c2f027ba46103f9d9 Mon Sep 17 00:00:00 2001 From: MarvelTiter_yaoqinglin Date: Fri, 23 Aug 2024 18:04:21 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0Builder=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/AutoGenMapper.Roslyn/BuildAutoMapClass.cs | 44 +- src/Generators.Shared/Builder/ClassBuilder.cs | 115 +++-- src/Generators.Shared/Builder/CodeFile.cs | 37 +- src/Generators.Shared/Builder/FieldBuilder.cs | 56 ++- src/Generators.Shared/Builder/Member.cs | 14 +- .../Builder/MethodBuilder.cs | 206 ++++++--- .../Builder/NamespaceBuilder.cs | 18 +- src/Generators.Shared/Builder/TypeBuilder.cs | 88 ++-- .../Extensions/ClassBuilderExtensions.cs | 44 +- .../Extensions/MethodBuilderExtensions.cs | 153 +++++-- .../Extensions/NameSpaceBuilderExtensions.cs | 16 +- .../Extensions/PropertyBuilderExtensions.cs | 33 ++ .../Extensions/TypeBuilderExtensions.cs | 115 ++--- src/Generators.Shared/MT.Generators.props | 3 +- .../Models/TypeParameterInfo.cs | 2 +- src/Generators.Shared/RoslynExtensions.cs | 425 ++++++++++-------- src/Generators.Shared/StringExtensions.cs | 11 +- 17 files changed, 810 insertions(+), 570 deletions(-) create mode 100644 src/Generators.Shared/Extensions/PropertyBuilderExtensions.cs diff --git a/src/AutoGenMapper.Roslyn/BuildAutoMapClass.cs b/src/AutoGenMapper.Roslyn/BuildAutoMapClass.cs index fa5a7a9..e23d0f7 100644 --- a/src/AutoGenMapper.Roslyn/BuildAutoMapClass.cs +++ b/src/AutoGenMapper.Roslyn/BuildAutoMapClass.cs @@ -115,18 +115,20 @@ private static bool GetPropertyValue(GenMapperContext context, IPropertySymbol p { if (prop.Type.HasInterfaceAll("System.Collections.IEnumerable") && prop.Type.SpecialType == SpecialType.None) { - var et = prop.Type.GetGenericTypes().First(); + ITypeSymbol? et = null; + var fin = ""; + if (prop.Type is IArrayTypeSymbol at) + { + et = at.ElementType; + fin = "ToArray()"; + } + else + { + et = prop.Type.GetGenericTypes().First(); + fin = "ToList()"; + } if (et.HasInterface(AutoMapperGenerator.GenMapableInterface)) { - var fin = ""; - if (prop.Type.Name.Contains("Array")) - { - fin = "ToArray()"; - } - else - { - fin = "ToList()"; - } value = ($"""this.{customTrans.From}.Select(i => i.MapTo<{et.ToDisplayString()}>("{et.MetadataName}")).{fin}"""); } else @@ -147,18 +149,20 @@ private static bool GetPropertyValue(GenMapperContext context, IPropertySymbol p { if (prop.Type.HasInterfaceAll("System.Collections.IEnumerable") && prop.Type.SpecialType == SpecialType.None) { - var et = prop.Type.GetGenericTypes().First(); + ITypeSymbol? et = null; + var fin = ""; + if (prop.Type is IArrayTypeSymbol at) + { + et = at.ElementType; + fin = "ToArray()"; + } + else + { + et = prop.Type.GetGenericTypes().First(); + fin = "ToList()"; + } if (et.HasInterface(AutoMapperGenerator.GenMapableInterface)) { - var fin = ""; - if (prop.Type.Name.Contains("Array")) - { - fin = "ToArray()"; - } - else - { - fin = "ToList()"; - } value = ($"""this.{p.Name}.Select(i => i.MapTo<{et.ToDisplayString()}>("{et.MetadataName}")).{fin}"""); } else diff --git a/src/Generators.Shared/Builder/ClassBuilder.cs b/src/Generators.Shared/Builder/ClassBuilder.cs index db04471..ac78dc3 100644 --- a/src/Generators.Shared/Builder/ClassBuilder.cs +++ b/src/Generators.Shared/Builder/ClassBuilder.cs @@ -1,85 +1,82 @@ using Microsoft.CodeAnalysis; -using System; -using System.CodeDom.Compiler; using System.Collections.Generic; using System.Linq; -using System.Text; -namespace Generators.Shared.Builder +namespace Generators.Shared.Builder; + +internal enum NodeType +{ + Unit, + NameSpace, + Class, + Interface, + Record, + Constructor, + Field, + Property, + Method, +} +internal enum MemberType { - internal enum NodeType + Field, + Property, + Method, + Constructor, +} +internal class ClassBuilder : MemberBuilder +{ + public ClassBuilder() { - Unit, - NameSpace, - Class, - Interface, - Record, - Constructor, - Field, - Property, - Method, + Modifiers = "public partial"; } - internal enum MemberType + public override NodeType Type => NodeType.Class; + public override string Indent => " "; + public string? BaseType { get; set; } + public string ClassType { get; set; } = "class"; + public IList Interfaces { get; } = []; + private string BaseTypeList { - Field, - Property, - Method, - Constructor, + get + { + if (BaseType == null && !Interfaces.Any()) + { + return ""; + } + string?[] all = [BaseType, .. Interfaces]; + return $": {string.Join(", ", all.Where(b => !string.IsNullOrEmpty(b)))}"; + } } - internal class ClassBuilder : MemberBuilder + + private IEnumerable RenderMembers() { - public ClassBuilder() + foreach (var m in Members.Where(m => m.Type == NodeType.Field)) { - Modifiers = "public partial"; + yield return $"{m}"; } - public override NodeType Type => NodeType.Class; - public override string Indent => " "; - public string? BaseType { get; set; } - public IList Interfaces { get; } = []; - private string BaseTypeList + foreach (var m in Members.Where(m => m.Type == NodeType.Constructor)) { - get - { - if (BaseType == null && !Interfaces.Any()) - { - return ""; - } - string?[] all = [BaseType, .. Interfaces]; - return $": {string.Join(", ", all.Where(b => !string.IsNullOrEmpty(b)))}"; - } + yield return $"{m}"; } - - private IEnumerable RenderMembers() + foreach (var m in Members.Where(m => m.Type == NodeType.Property)) { - foreach (var m in Members.Where(m => m.Type == NodeType.Field)) - { - yield return $"{m}"; - } - foreach (var m in Members.Where(m => m.Type == NodeType.Constructor)) - { - yield return $"{m}"; - } - foreach (var m in Members.Where(m => m.Type == NodeType.Property)) - { - yield return $"{m}"; - } - foreach (var m in Members.Where(m => m.Type == NodeType.Method)) - { - yield return $"{m}"; - } + yield return $"{m}"; } - - public override string ToString() + foreach (var m in Members.Where(m => m.Type == NodeType.Method)) { - return + yield return $"{m}"; + } + } + + public override string ToString() + { + return $$""" {{AttributeList}} {{Indent}}/// -{{Indent}}{{Modifiers}} class {{Name}} {{BaseTypeList}} +{{Indent}}{{Modifiers}} {{ClassType}} {{Name}} {{BaseTypeList}} {{Indent}}{ {{string.Join("\n\n", RenderMembers())}} {{Indent}}} """; - } } } diff --git a/src/Generators.Shared/Builder/CodeFile.cs b/src/Generators.Shared/Builder/CodeFile.cs index ae18392..d107753 100644 --- a/src/Generators.Shared/Builder/CodeFile.cs +++ b/src/Generators.Shared/Builder/CodeFile.cs @@ -2,33 +2,32 @@ using System.Collections.Generic; using System.Linq; -namespace Generators.Shared.Builder +namespace Generators.Shared.Builder; + +internal class CodeFile : TypeBuilder { - internal class CodeFile : TypeBuilder - { - private CodeFile(string filename) { FileName = filename; } - public static CodeFile New(string filename) => new(filename); - public override NodeType Type => NodeType.Unit; - public string FileName { get; set; } - public IList Usings { get; } = []; + private CodeFile(string filename) { FileName = filename; } + public static CodeFile New(string filename) => new(filename); + public override NodeType Type => NodeType.Unit; + public string FileName { get; set; } + public IList Usings { get; } = []; - public CodeFile AddUsings(params string[] usings) + public CodeFile AddUsings(params string[] usings) + { + foreach (var us in usings) { - foreach (var us in usings) - { - Usings.Add(us); - } - return this; + Usings.Add(us); } + return this; + } - public override string ToString() - { - return + public override string ToString() + { + return $""" {string.Join("\n", Usings)} {string.Join("\n\n", Members.Select(m => m.ToString()))} """ - ; - } + ; } } diff --git a/src/Generators.Shared/Builder/FieldBuilder.cs b/src/Generators.Shared/Builder/FieldBuilder.cs index 1cc2b46..e04ec5e 100644 --- a/src/Generators.Shared/Builder/FieldBuilder.cs +++ b/src/Generators.Shared/Builder/FieldBuilder.cs @@ -2,36 +2,50 @@ using System.Collections.Generic; using System.Text; -namespace Generators.Shared.Builder +namespace Generators.Shared.Builder; + +internal class FieldBuilder : MemberBuilder { - internal class FieldBuilder : MemberBuilder + public FieldBuilder() { - public FieldBuilder() - { - Modifiers = "private readonly"; - } - public override NodeType Type => NodeType.Field; - public override string Indent => " "; - public override string ToString() - { - return $""" - {Indent}{Modifiers} {MemberType} {Name}; - """; - } + Modifiers = "private readonly"; + } + public override NodeType Type => NodeType.Field; + public override string Indent => " "; + public override string ToString() + { + return $""" + {Indent}{Modifiers} {MemberType} {Name}; + """; } +} - internal class PropertyBuilder : MemberBuilder +internal class PropertyBuilder : MemberBuilder +{ + public PropertyBuilder() { - public PropertyBuilder() + Modifiers = "public"; + } + public bool CanRead { get; set; } = true; + public bool CanWrite { get; set; } = true; + public override NodeType Type => NodeType.Property; + public override string Indent => " "; + string Getter => CanRead ? " get;" : ""; + string Setter => CanWrite ? " set;" : ""; + public bool IsLambdaBody { get; set; } + string InitStatement => string.IsNullOrEmpty(Initialization) ? "" : $" = {Initialization};"; + public override string ToString() + { + if (IsLambdaBody) { - Modifiers = "public"; + return $$""" + {{Indent}}{{Modifiers}} {{MemberType}} {{Name}} => {{Initialization}}; + """; } - public override NodeType Type => NodeType.Property; - public override string Indent => " "; - public override string ToString() + else { return $$""" - {{Indent}}{{Modifiers}} {{MemberType}} {{Name}} { get; set; } + {{Indent}}{{Modifiers}} {{MemberType}} {{Name}} {{{Getter}}{{Setter}} }{{InitStatement}} """; } } diff --git a/src/Generators.Shared/Builder/Member.cs b/src/Generators.Shared/Builder/Member.cs index 4542570..d37170f 100644 --- a/src/Generators.Shared/Builder/Member.cs +++ b/src/Generators.Shared/Builder/Member.cs @@ -1,9 +1,9 @@ -namespace Generators.Shared.Builder + +namespace Generators.Shared.Builder; + +internal abstract class Node { - internal abstract class Node - { - public abstract NodeType Type { get; } - public virtual string Indent => ""; - //public abstract new string ToString(); - } + public abstract NodeType Type { get; } + public virtual string Indent => ""; + //public abstract new string ToString(); } diff --git a/src/Generators.Shared/Builder/MethodBuilder.cs b/src/Generators.Shared/Builder/MethodBuilder.cs index 25424a8..be018a0 100644 --- a/src/Generators.Shared/Builder/MethodBuilder.cs +++ b/src/Generators.Shared/Builder/MethodBuilder.cs @@ -3,31 +3,31 @@ using System.Linq; using System.Text; -namespace Generators.Shared.Builder +namespace Generators.Shared.Builder; + +internal class MethodBuilder : MethodBase { - internal class MethodBuilder : MethodBase + public MethodBuilder() { - public MethodBuilder() - { - Modifiers = "public"; - } - public override NodeType Type => NodeType.Method; - public bool IsAsync { get; set; } - public bool IsLambdaBody { get; set; } - string Async => IsAsync ? " async " : " "; - public string? ReturnType { get; set; } = "void"; - - public override string ToString() - { - if (IsLambdaBody) - return + Modifiers = "public"; + } + public override NodeType Type => NodeType.Method; + public bool IsAsync { get; set; } + public bool IsLambdaBody { get; set; } + string Async => IsAsync ? " async " : " "; + public string? ReturnType { get; set; } = "void"; + + public override string ToString() + { + if (IsLambdaBody) + return $$""" {{AttributeList}} {{Indent}}{{Modifiers}}{{Async}}{{ReturnType}} {{Name}}{{Types}}({{string.Join(", ", Parameters)}}){{TypeConstraints}} {{Indent}} => {{Body.FirstOrDefault()?.ToString().Trim()}} """; - else - return + else + return $$""" {{AttributeList}} {{Indent}}{{Modifiers}}{{Async}}{{ReturnType}} {{Name}}{{Types}}({{string.Join(", ", Parameters)}}){{TypeConstraints}} @@ -35,61 +35,159 @@ public override string ToString() {{string.Join("\n", Body)}} {{Indent}}} """; - } } +} - internal class ConstructorBuilder : MethodBase +internal class ConstructorBuilder : MethodBase +{ + public ConstructorBuilder() { - public ConstructorBuilder() - { - Modifiers = "public"; - } - public override NodeType Type => NodeType.Constructor; - public override string ToString() - { - return + Modifiers = "public"; + } + public override NodeType Type => NodeType.Constructor; + public override string ToString() + { + return $$""" {{Indent}}{{Modifiers}} {{Name}}({{string.Join(", ", Parameters)}}) {{Indent}}{ {{string.Join("\n", Body)}} {{Indent}}} """; - } } +} - internal class Statement +internal class Statement +{ + public string Indent => " "; + + public string Content { get; } + + public Statement(string content) + { + Content = content; + } + public static implicit operator Statement(string content) => new Statement(content); + + public override string ToString() { - public string Indent => " "; + //if (!Content.Trim().EndsWith(";")) + //{ + // return $"{Indent}{Content};"; + //} + return $"{Indent}{Content}{AttachSemicolon()}"; + } - public string Content { get; } + private string AttachSemicolon() + { + if (Content.Trim().EndsWith(";")) + return string.Empty; + if (Content.StartsWith("if")) + return string.Empty; + if (Content.StartsWith("{")) + return string.Empty; + if (Content.EndsWith("}")) + return string.Empty; + return ";"; - public Statement(string content) + } +} + +internal class SwitchStatement : Statement +{ + public SwitchStatement(string switchValue) : base("") + { + SwitchValue = switchValue; + } + public SwitchStatement() : base("") + { + + } + public static SwitchStatement Default => new SwitchStatement(); + + public string? SwitchValue { get; set; } + public List SwitchCases { get; set; } = []; + public DefaultCaseStatement? DefaultCase { get; set; } + public override string ToString() + { + return +$$""" +{{Indent}}switch ({{SwitchValue}}) +{{Indent}}{ +{{string.Join("\n", SwitchCases)}} +{{DefaultCase}} +{{Indent}}} +"""; + } +} + +internal class SwitchCaseStatement : Statement +{ + public SwitchCaseStatement() : base("") + { + + } + public string? Condition { get; set; } + public string? Action { get; set; } + public bool IsBreak { get; set; } + public override string ToString() + { + if (IsBreak) { - Content = content; + return +$""" +{Indent} case {Condition}: +{Indent} {Action}; +{Indent} break; +"""; } - public static implicit operator Statement(string content) => new Statement(content); - - public override string ToString() + else { - //if (!Content.Trim().EndsWith(";")) - //{ - // return $"{Indent}{Content};"; - //} - return $"{Indent}{Content}{AttachSemicolon()}"; + return +$""" +{Indent} case {Condition}: +{Indent} {Action}; +"""; } + } +} - private string AttachSemicolon() - { - if (Content.Trim().EndsWith(";")) - return string.Empty; - if (Content.StartsWith("if")) - return string.Empty; - if (Content.StartsWith("{")) - return string.Empty; - if (Content.EndsWith("}")) - return string.Empty; - return ";"; +internal class DefaultCaseStatement : Statement +{ + public DefaultCaseStatement() : base("") + { - } + } + public string? Condition { get; set; } + public string? Action { get; set; } + public override string ToString() + { + return +$""" +{Indent} default: +{Indent} {Action}; +"""; + } + public static implicit operator DefaultCaseStatement(string action) => new DefaultCaseStatement { Action = action }; +} + +internal class IfStatement : Statement +{ + public IfStatement() : base("") + { + + } + internal static IfStatement Default => new IfStatement(); + public string? Condition { get; set; } + public List IfContents { get; set; } = []; + public override string ToString() + { + return +$$""" +{{Indent}}if ({{Condition}}) +{{Indent}}{ +{{string.Join("\n", IfContents.Select(s => $" {s}"))}} +{{Indent}}} +"""; } } diff --git a/src/Generators.Shared/Builder/NamespaceBuilder.cs b/src/Generators.Shared/Builder/NamespaceBuilder.cs index 4e2fa49..d52ad9a 100644 --- a/src/Generators.Shared/Builder/NamespaceBuilder.cs +++ b/src/Generators.Shared/Builder/NamespaceBuilder.cs @@ -1,24 +1,24 @@ using Microsoft.CodeAnalysis; using System.Linq; -namespace Generators.Shared.Builder +namespace Generators.Shared.Builder; + +internal class NamespaceBuilder : TypeBuilder { - internal class NamespaceBuilder : TypeBuilder + public NamespaceBuilder() { } + public override NodeType Type => NodeType.NameSpace; + public string? Namespace { get; set; } + public override string ToString() { - public NamespaceBuilder() { } - public override NodeType Type => NodeType.NameSpace; - public string? Namespace { get; set; } - public override string ToString() - { - return + return $$""" // #pragma warning disable +#nullable enable namespace {{Namespace}} { {{string.Join("\t", Members)}} } """; - } } } diff --git a/src/Generators.Shared/Builder/TypeBuilder.cs b/src/Generators.Shared/Builder/TypeBuilder.cs index 17f1edd..dfda864 100644 --- a/src/Generators.Shared/Builder/TypeBuilder.cs +++ b/src/Generators.Shared/Builder/TypeBuilder.cs @@ -1,58 +1,58 @@ -using Generators.Shared.Models; +using Generators.Models; using System.Collections.Generic; using System.Linq; -namespace Generators.Shared.Builder +namespace Generators.Shared.Builder; + +internal abstract class TypeBuilder : Node { - internal abstract class TypeBuilder : Node - { - public IList Members { get; } = []; - } - internal abstract class TypeBuilder : TypeBuilder where T : TypeBuilder, new() - { - public static T Default => new T(); - } + public IList Members { get; } = []; +} +internal abstract class TypeBuilder : TypeBuilder where T : TypeBuilder, new() +{ + public static T Default => new T(); +} - internal abstract class MemberBuilder : TypeBuilder +internal abstract class MemberBuilder : TypeBuilder +{ + public string? Name { get; set; } + public string? Modifiers { get; set; } + public string? MemberType { get; set; } + public string? Initialization { get; set; } + public IList Attributes { get; } = []; + public bool IsGeneric { get; set; } + public IList TypeArguments { get; set; } = []; + protected string AttributeList => string.Join("\n", Attributes.Select(a => $"{Indent}[{a}]")); + protected string Types { - public string? Name { get; set; } - public string? Modifiers { get; set; } - public string? MemberType { get; set; } - public IList Attributes { get; } = []; - public bool IsGeneric { get; set; } - public IList TypeArguments { get; set; } = []; - protected string AttributeList => string.Join("\n", Attributes.Select(a => $"{Indent}[{a}]")); - protected string Types + get { - get - { - if (TypeArguments.Count == 0) return ""; - return $"<{string.Join(", ", TypeArguments.Select(ta => ta.Name))}>"; - } + if (TypeArguments.Count == 0) return ""; + return $"<{string.Join(", ", TypeArguments.Select(ta => ta.Name))}>"; } - protected string TypeConstraints + } + protected string TypeConstraints + { + get { - get - { - if (TypeArguments.Count == 0) return ""; - return $"\n{string.Join("\n", TypeArguments.Select(ta => $"{Indent} where {ta.Name} : {string.Join(", ", ta.Constraints)}"))}"; - } + if (TypeArguments.Count == 0) return ""; + return $"\n{string.Join("\n", TypeArguments.Select(ta => $"{Indent} where {ta.Name} : {string.Join(", ", ta.Constraints)}"))}"; } } +} - internal abstract class MemberBuilder : MemberBuilder where T : MemberBuilder, new() - { - public static T Default => new T(); - } +internal abstract class MemberBuilder : MemberBuilder where T : MemberBuilder, new() +{ + public static T Default => new T(); +} - internal abstract class MethodBase : MemberBuilder - { - public IList Parameters { get; set; } = []; - public IList Body { get; set; } = []; - } - internal abstract class MethodBase : MethodBase where T : MethodBase, new() - { - public static T Default => new T(); - public override string Indent => " "; - } +internal abstract class MethodBase : MemberBuilder +{ + public IList Parameters { get; set; } = []; + public IList Body { get; set; } = []; +} +internal abstract class MethodBase : MethodBase where T : MethodBase, new() +{ + public static T Default => new T(); + public override string Indent => " "; } diff --git a/src/Generators.Shared/Extensions/ClassBuilderExtensions.cs b/src/Generators.Shared/Extensions/ClassBuilderExtensions.cs index b13bb75..9a637ce 100644 --- a/src/Generators.Shared/Extensions/ClassBuilderExtensions.cs +++ b/src/Generators.Shared/Extensions/ClassBuilderExtensions.cs @@ -1,27 +1,33 @@ -namespace Generators.Shared.Builder +using Generators.Shared.Builder; + +namespace Generators.Shared; + +internal static class ClassBuilderExtensions { - internal static class ClassBuilderExtensions + public static ClassBuilder ClassName(this ClassBuilder builder, string className) { - public static ClassBuilder ClassName(this ClassBuilder builder, string className) - { - builder.Name = className; - return builder; - } + builder.Name = className; + return builder; + } - - public static ClassBuilder BaseType(this ClassBuilder builder, string baseType) - { - builder.BaseType = baseType; - return builder; - } + public static ClassBuilder MakeRecord(this ClassBuilder builder) + { + builder.ClassType = "record"; + return builder; + } + + public static ClassBuilder BaseType(this ClassBuilder builder, string baseType) + { + builder.BaseType = baseType; + return builder; + } - public static ClassBuilder Interface(this ClassBuilder builder, params string[] interfaces) + public static ClassBuilder Interface(this ClassBuilder builder, params string[] interfaces) + { + foreach (var i in interfaces) { - foreach (var i in interfaces) - { - builder.Interfaces.Add(i); - } - return builder; + builder.Interfaces.Add(i); } + return builder; } } diff --git a/src/Generators.Shared/Extensions/MethodBuilderExtensions.cs b/src/Generators.Shared/Extensions/MethodBuilderExtensions.cs index 4d72746..f0a197f 100644 --- a/src/Generators.Shared/Extensions/MethodBuilderExtensions.cs +++ b/src/Generators.Shared/Extensions/MethodBuilderExtensions.cs @@ -1,74 +1,129 @@ using Generators.Shared.Builder; -using Generators.Shared.Models; +using Generators.Models; using System; using System.Collections.Generic; using System.Text; -namespace Generators.Shared.Builder +namespace Generators.Shared; + +internal static class MethodBuilderExtensions { - internal static class MethodBuilderExtensions + public static T MethodName(this T builder, string name) where T : MethodBase { - public static T MethodName(this T builder, string name) where T : MethodBase - { - builder.Name = name; - return builder; - } - public static T Generic(this T builder,params TypeParameterInfo[] types) where T : MemberBuilder + builder.Name = name; + return builder; + } + public static T Generic(this T builder, params TypeParameterInfo[] types) where T : MemberBuilder + { + if (types.Length > 0) { - if (types.Length > 0) + builder.IsGeneric = true; + foreach (var item in types) { - builder.IsGeneric = true; - foreach (var item in types) - { - builder.TypeArguments.Add(item); - } + builder.TypeArguments.Add(item); } - return builder; - } - public static MethodBuilder ReturnType(this MethodBuilder builder, string returnType) - { - builder.ReturnType = returnType; - return builder; - } - public static MethodBuilder Async(this MethodBuilder builder) - { - builder.IsAsync = true; - return builder; } + return builder; + } + public static MethodBuilder ReturnType(this MethodBuilder builder, string returnType) + { + builder.ReturnType = returnType; + return builder; + } + public static MethodBuilder Async(this MethodBuilder builder) + { + builder.IsAsync = true; + return builder; + } + + public static MethodBuilder Lambda(this MethodBuilder builder, string body) + { + builder.IsLambdaBody = true; + builder.Body.Add(body); + return builder; + } - public static MethodBuilder Lambda(this MethodBuilder builder, string body) + public static T AddBody(this T builder, params string[] body) where T : MethodBase + { + foreach (var item in body) { - builder.IsLambdaBody = true; - builder.Body.Add(body); - return builder; + builder.Body.Add(item); } + return builder; + } - public static T AddBody(this T builder, params string[] body) where T : MethodBase + public static T AddBody(this T builder, params Statement[] body) where T : MethodBase + { + foreach (var item in body) { - foreach (var item in body) - { - builder.Body.Add(item); - } - return builder; + builder.Body.Add(item); } + return builder; + } - public static T AddBody(this T builder, params Statement[] body) where T : MethodBase + public static T AddParameter(this T builder, params string[] parameters) + where T : MethodBase + { + foreach (var item in parameters) { - foreach (var item in body) - { - builder.Body.Add(item); - } - return builder; + builder.Parameters.Add(item); } + return builder; + } + #region switch + public static T AddSwitchStatement(this T builder, string switchValue, Action action) where T : MethodBase + { + var switchStatement = SwitchStatement.Default.Switch(switchValue); + action.Invoke(switchStatement); + builder.AddBody(switchStatement); + return builder; + } + + public static SwitchStatement Switch(this SwitchStatement switchStatement, string switchValue) + { + switchStatement.SwitchValue = switchValue; + return switchStatement; + } + + public static SwitchStatement AddReturnCase(this SwitchStatement switchStatement, string condition, string returnItem) + { + switchStatement.SwitchCases.Add(new SwitchCaseStatement { Condition = condition, Action = $"return {returnItem}" }); + return switchStatement; + } + + public static SwitchStatement AddBreakCase(this SwitchStatement switchStatement, string condition, string action) + { + switchStatement.SwitchCases.Add(new SwitchCaseStatement { Condition = condition, Action = action, IsBreak = true }); + return switchStatement; + } + public static SwitchStatement AddDefaultCase(this SwitchStatement switchStatement, string action) + { + switchStatement.DefaultCase = new DefaultCaseStatement { Action = action }; + return switchStatement; + } + #endregion + + #region if + public static T AddIfStatement(this T builder, string condition, Action action) where T : MethodBase + { + var ifs = IfStatement.Default.If(condition); + action.Invoke(ifs); + builder.AddBody(ifs); + return builder; + } + public static IfStatement If(this IfStatement ifStatement, string condition) + { + ifStatement.Condition = condition; + return ifStatement; + } - public static T AddParameter(this T builder, params string[] parameters) - where T : MethodBase + public static IfStatement AddStatement(this IfStatement ifStatement, params string[] statements) + { + foreach (var statement in statements) { - foreach (var item in parameters) - { - builder.Parameters.Add(item); - } - return builder; + ifStatement.IfContents.Add(statement); } + return ifStatement; } + #endregion } diff --git a/src/Generators.Shared/Extensions/NameSpaceBuilderExtensions.cs b/src/Generators.Shared/Extensions/NameSpaceBuilderExtensions.cs index c382c20..035fb63 100644 --- a/src/Generators.Shared/Extensions/NameSpaceBuilderExtensions.cs +++ b/src/Generators.Shared/Extensions/NameSpaceBuilderExtensions.cs @@ -1,11 +1,13 @@ -namespace Generators.Shared.Builder +using Generators.Shared; +using Generators.Shared.Builder; + +namespace Generators.Shared; + +internal static class NameSpaceBuilderExtensions { - internal static class NameSpaceBuilderExtensions + public static NamespaceBuilder Namespace(this NamespaceBuilder builder, string @namespace) { - public static NamespaceBuilder Namespace(this NamespaceBuilder builder, string @namespace) - { - builder.Namespace = @namespace; - return builder; - } + builder.Namespace = @namespace; + return builder; } } diff --git a/src/Generators.Shared/Extensions/PropertyBuilderExtensions.cs b/src/Generators.Shared/Extensions/PropertyBuilderExtensions.cs new file mode 100644 index 0000000..fc68fdc --- /dev/null +++ b/src/Generators.Shared/Extensions/PropertyBuilderExtensions.cs @@ -0,0 +1,33 @@ +using Generators.Shared.Builder; + +namespace Generators.Shared; + +internal static class PropertyBuilderExtensions +{ + public static PropertyBuilder PropertyName(this PropertyBuilder builder, string name) + { + builder.Name = name; + return builder; + } + public static PropertyBuilder Readonly(this PropertyBuilder builder) + { + builder.CanRead = true; + builder.CanWrite = false; + return builder; + } + + public static PropertyBuilder Writeonly(this PropertyBuilder builder) + { + builder.CanRead = false; + builder.CanWrite = true; + return builder; + } + + public static PropertyBuilder Lambda(this PropertyBuilder builder, string body) + { + builder.IsLambdaBody = true; + builder.Readonly(); + builder.InitializeWith(body); + return builder; + } +} diff --git a/src/Generators.Shared/Extensions/TypeBuilderExtensions.cs b/src/Generators.Shared/Extensions/TypeBuilderExtensions.cs index c3cf35e..1923620 100644 --- a/src/Generators.Shared/Extensions/TypeBuilderExtensions.cs +++ b/src/Generators.Shared/Extensions/TypeBuilderExtensions.cs @@ -1,76 +1,77 @@ -using Microsoft.CodeAnalysis; +using Generators.Shared.Builder; +using Microsoft.CodeAnalysis; using System; -namespace Generators.Shared.Builder +namespace Generators.Shared; + +internal static class TypeBuilderExtensions { - internal static class TypeBuilderExtensions + public static void AddSource(this SourceProductionContext context, CodeFile? codeFile) { - public static void AddSource(this SourceProductionContext context, CodeFile? codeFile) - { - if (codeFile is null) return; - context.AddSource(codeFile.FileName, codeFile.ToString()); - } - public static T AddMembers(this T builder, params Node[] members) where T : TypeBuilder - { - foreach (var item in members) - { - builder.Members.Add(item); - } - return builder; - } - public static T Modifiers(this T builder, string modifiers) where T : MemberBuilder + if (codeFile is null) return; + context.AddSource(codeFile.FileName, codeFile.ToString()); + } + public static T AddMembers(this T builder, params Node[] members) where T : TypeBuilder + { + foreach (var item in members) { - builder.Modifiers = modifiers; - return builder; + builder.Members.Add(item); } + return builder; + } + public static T Modifiers(this T builder, string modifiers) where T : MemberBuilder + { + builder.Modifiers = modifiers; + return builder; + } - public static T Attribute(this T builder, params string[] attributes) where T : MemberBuilder + public static T Attribute(this T builder, params string[] attributes) where T : MemberBuilder + { + foreach (var attr in attributes) { - foreach (var attr in attributes) - { - builder.Attributes.Add(attr); - } - return builder; + builder.Attributes.Add(attr); } + return builder; + } - public static T AttributeIf(this T builder, bool condition, params string[] attributes) - where T : MemberBuilder - { - if (condition) - { - builder.Attribute(attributes); - } - return builder; - } + public static T InitializeWith(this T builder, string statement) where T : MemberBuilder + { + builder.Initialization = statement; + return builder; + } - public static T AddGeneratedCodeAttribute(this T builder, Type generatorType) where T : MemberBuilder + public static T AttributeIf(this T builder, bool condition, params string[] attributes) + where T : MemberBuilder + { + if (condition) { - return builder.Attribute($"""global::System.CodeDom.Compiler.GeneratedCode("{generatorType.FullName}", "{generatorType.Assembly.GetName().Version}")"""); - //return builder; + builder.Attribute(attributes); } + return builder; + } - public static FieldBuilder MemberType(this FieldBuilder builder, string type) - { - builder.MemberType = type; - return builder; - } + public static T AddGeneratedCodeAttribute(this T builder, Type generatorType) where T : MemberBuilder + { + return builder.Attribute($"""global::System.CodeDom.Compiler.GeneratedCode("{generatorType.FullName}", "{generatorType.Assembly.GetName().Version}")"""); + //return builder; + } - public static PropertyBuilder MemberType(this PropertyBuilder builder, string type) - { - builder.MemberType = type; - return builder; - } + public static FieldBuilder MemberType(this FieldBuilder builder, string type) + { + builder.MemberType = type; + return builder; + } - public static FieldBuilder FieldName(this FieldBuilder builder, string name) - { - builder.Name = name; - return builder; - } + public static PropertyBuilder MemberType(this PropertyBuilder builder, string type) + { + builder.MemberType = type; + return builder; + } - public static PropertyBuilder PropertyName(this PropertyBuilder builder, string name) - { - builder.Name = name; - return builder; - } + public static FieldBuilder FieldName(this FieldBuilder builder, string name) + { + builder.Name = name; + return builder; } + } diff --git a/src/Generators.Shared/MT.Generators.props b/src/Generators.Shared/MT.Generators.props index eead6db..0595c51 100644 --- a/src/Generators.Shared/MT.Generators.props +++ b/src/Generators.Shared/MT.Generators.props @@ -6,8 +6,7 @@ true latest true - enable - + enable diff --git a/src/Generators.Shared/Models/TypeParameterInfo.cs b/src/Generators.Shared/Models/TypeParameterInfo.cs index ff9e373..7c14e7b 100644 --- a/src/Generators.Shared/Models/TypeParameterInfo.cs +++ b/src/Generators.Shared/Models/TypeParameterInfo.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.Text; -namespace Generators.Shared.Models +namespace Generators.Models { internal class TypeParameterInfo(string name, string[] constraints) { diff --git a/src/Generators.Shared/RoslynExtensions.cs b/src/Generators.Shared/RoslynExtensions.cs index 0b61dab..4ed10f4 100644 --- a/src/Generators.Shared/RoslynExtensions.cs +++ b/src/Generators.Shared/RoslynExtensions.cs @@ -1,4 +1,4 @@ -using Generators.Shared.Models; +using Generators.Models; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; @@ -8,265 +8,298 @@ using System.Linq; using System.Threading; -namespace Generators.Shared +namespace Generators.Shared; + +internal static class RoslynExtensions { - internal static class RoslynExtensions + /// + /// 获取指定了名称的参数的值 + /// + /// + /// + /// + public static object? GetNamedValue(this AttributeData? a, string key) { - /// - /// 获取指定了名称的参数的值 - /// - /// - /// - /// - public static object? GetNamedValue(this AttributeData? a, string key) - { - if (a == null) return null; - var named = a.NamedArguments.FirstOrDefault(t => t.Key == key); - return named.Value.Value; - } - /// - /// 获取指定了名称的参数的值 - /// - /// - /// - /// - public static bool GetNamedValue(this AttributeData? a, string key, out object? value) + if (a == null) return null; + var named = a.NamedArguments.FirstOrDefault(t => t.Key == key); + return named.Value.Value; + } + /// + /// 获取指定了名称的参数的值 + /// + /// + /// + /// + public static bool GetNamedValue(this AttributeData? a, string key, out object? value) + { + var t = a.GetNamedValue(key); + value = t; + return t != null; + } + + public static bool GetConstructorValue(this AttributeData a, int index, out object? value) + { + if (a.ConstructorArguments.Length <= index) { - var t = GetNamedValue(a, key); - value = t; - return t != null; + value = null; + return false; } - /// - /// 根据名称获取attribute的值 - /// - /// - /// - /// - /// - public static bool GetAttribute(this ISymbol? symbol, string fullName, out AttributeData? data) + value = a.ConstructorArguments[index].Value; + return true; + } + + public static bool GetConstructorValues(this AttributeData a, int index, out object?[] values) + { + if (a.ConstructorArguments.Length <= index) { - data = symbol?.GetAttributes().FirstOrDefault(a => a.AttributeClass?.ToDisplayString() == fullName); - return data != null; + values = []; + return false; } - /// - /// 根据名称获取attribute的值 - /// - /// - /// - /// - /// - public static IEnumerable GetAttributes(this ISymbol? symbol, string fullName) + values = a.ConstructorArguments[index].Values.Select(v => v.Value).ToArray(); + return true; + } + + /// + /// 根据名称获取attribute的值 + /// + /// + /// + /// + /// + public static bool GetAttribute(this ISymbol? symbol, string fullName, out AttributeData? data) + { + data = symbol?.GetAttributes().FirstOrDefault(a => a.AttributeClass?.ToDisplayString() == fullName); + return data != null; + } + /// + /// 根据名称获取attribute的值 + /// + /// + /// + /// + /// + public static IEnumerable GetAttributes(this ISymbol? symbol, string fullName) + { + foreach (var item in symbol?.GetAttributes() ?? []) { - foreach (var item in symbol?.GetAttributes() ?? []) + if (item.AttributeClass?.ToDisplayString() == fullName) { - if (item.AttributeClass?.ToDisplayString() == fullName) - { - yield return item; - } + yield return item; } } - /// - /// 根据名称判定是否拥有某个attribute - /// - /// - /// - /// - /// - public static bool HasAttribute(this ISymbol? symbol, string fullName) - { - return symbol?.GetAttributes().Any(a => a.AttributeClass?.ToDisplayString() == fullName) == true; - } + } + /// + /// 根据名称判定是否拥有某个attribute + /// + /// + /// + /// + /// + public static bool HasAttribute(this ISymbol? symbol, string fullName) + { + return symbol?.GetAttributes().Any(a => a.AttributeClass?.ToDisplayString() == fullName) == true; + } - public static bool HasInterface(this ITypeSymbol? symbol, string fullName) - { - return symbol?.Interfaces.Any(i => i.ToDisplayString() == fullName) == true; - } + public static bool HasInterface(this ITypeSymbol? symbol, string fullName) + { + return symbol?.Interfaces.Any(i => i.ToDisplayString() == fullName) == true; + } - public static bool HasInterfaceAll(this ITypeSymbol? symbol, string fullName) - { - return symbol?.AllInterfaces.Any(i => i.ToDisplayString() == fullName) == true; - } + public static bool HasInterfaceAll(this ITypeSymbol? symbol, string fullName) + { + return symbol?.AllInterfaces.Any(i => i.ToDisplayString() == fullName) == true; + } - public static bool CheckDisableGenerator(this AnalyzerConfigOptionsProvider options, string key) - { - return options.GlobalOptions.TryGetValue($"build_property.{key}", out var value) && !string.IsNullOrEmpty(value); - } + public static bool CheckDisableGenerator(this AnalyzerConfigOptionsProvider options, string key) + { + return options.GlobalOptions.TryGetValue($"build_property.{key}", out var value) && !string.IsNullOrEmpty(value); + } - public static string[] GetTargetUsings(this GeneratorAttributeSyntaxContext source) - { - if (source.TargetNode is + public static string[] GetTargetUsings(this GeneratorAttributeSyntaxContext source) + { + if (source.TargetNode is + { + Parent: NamespaceDeclarationSyntax { - Parent: NamespaceDeclarationSyntax + Usings: var nu, + Parent: CompilationUnitSyntax { - Usings: var nu, - Parent: CompilationUnitSyntax - { - Usings: var cnu - } + Usings: var cnu } } - ) - { - UsingDirectiveSyntax[] arr = [.. nu, .. cnu]; - return arr.Select(a => a.ToFullString().Replace("\n", "")).ToArray(); } - - return []; + ) + { + UsingDirectiveSyntax[] arr = [.. nu, .. cnu]; + return arr.Select(a => a.ToFullString().Replace("\n", "")).ToArray(); } - //public static string[] GetTargetUsings(this INamedTypeSymbol typeSymbol) + return []; + } + + //public static string[] GetTargetUsings(this INamedTypeSymbol typeSymbol) + //{ + // typeSymbol.ContainingNamespace.NamespaceKind + //} + + public static IEnumerable GetAllSymbols(this Compilation compilation, string fullName) + { + //var mainAsm = compilation.SourceModule.ContainingAssembly; + //var refAsmSymbols = compilation.SourceModule.ReferencedAssemblySymbols; + + //foreach (var asm in refAsmSymbols.Concat([mainAsm])) //{ - // typeSymbol.ContainingNamespace.NamespaceKind + // if (IsSystemType(asm)) + // { + // continue; + // } + // foreach (var item in GetAllSymbols(asm.GlobalNamespace)) + // { + // yield return item; + // } //} + return InternalGetAllSymbols(compilation.GlobalNamespace); - public static IEnumerable GetAllSymbols(this Compilation compilation, string fullName) + IEnumerable InternalGetAllSymbols(INamespaceSymbol global) { - //var mainAsm = compilation.SourceModule.ContainingAssembly; - //var refAsmSymbols = compilation.SourceModule.ReferencedAssemblySymbols; - - //foreach (var asm in refAsmSymbols.Concat([mainAsm])) - //{ - // if (IsSystemType(asm)) - // { - // continue; - // } - // foreach (var item in GetAllSymbols(asm.GlobalNamespace)) - // { - // yield return item; - // } - //} - return InternalGetAllSymbols(compilation.GlobalNamespace); - - IEnumerable InternalGetAllSymbols(INamespaceSymbol global) + foreach (var symbol in global.GetMembers()) { - foreach (var symbol in global.GetMembers()) + if (symbol is INamespaceSymbol n) { - if (symbol is INamespaceSymbol n) + foreach (var item in InternalGetAllSymbols(n)) { - foreach (var item in InternalGetAllSymbols(n)) - { - //if (item.HasAttribute(AutoInject)) - yield return item; - } - } - else if (symbol is INamedTypeSymbol target) - { - if (target.HasAttribute(fullName)) - yield return target; + //if (item.HasAttribute(AutoInject)) + yield return item; } } + else if (symbol is INamedTypeSymbol target) + { + if (target.HasAttribute(fullName)) + yield return target; + } } + } - bool IsSystemType(ISymbol symbol) - { - return symbol.Name == "System" || symbol.Name.Contains("System.") || symbol.Name.Contains("Microsoft."); - } - + bool IsSystemType(ISymbol symbol) + { + return symbol.Name == "System" || symbol.Name.Contains("System.") || symbol.Name.Contains("Microsoft."); } + } - public static string FormatClassName(this INamedTypeSymbol interfaceSymbol) + + public static string FormatClassName(this INamedTypeSymbol interfaceSymbol) + { + var meta = interfaceSymbol.MetadataName; + if (meta.IndexOf('`') > -1) { - var meta = interfaceSymbol.MetadataName; - if (meta.IndexOf('`') > -1) - { - meta = meta.Substring(0, meta.IndexOf('`')); - } - if (interfaceSymbol.TypeKind == TypeKind.Interface && meta.StartsWith("I")) - { - meta = meta.Substring(1); - } - return meta; + meta = meta.Substring(0, meta.IndexOf('`')); } + if (interfaceSymbol.TypeKind == TypeKind.Interface && meta.StartsWith("I")) + { + meta = meta.Substring(1); + } + return meta; + } - public static string FormatFileName(this INamedTypeSymbol interfaceSymbol) + public static string FormatFileName(this INamedTypeSymbol interfaceSymbol) + { + var meta = interfaceSymbol.MetadataName; + if (interfaceSymbol.TypeKind == TypeKind.Interface && meta.StartsWith("I")) { - var meta = interfaceSymbol.MetadataName; - if (interfaceSymbol.TypeKind == TypeKind.Interface && meta.StartsWith("I")) - { - meta = meta.Substring(1); - } - return meta; + meta = meta.Substring(1); } + return meta; + } - public static IEnumerable<(IMethodSymbol Symbol, AttributeData? AttrData)> GetAllMethodWithAttribute(this INamedTypeSymbol interfaceSymbol, string fullName, INamedTypeSymbol? classSymbol = null) + public static IEnumerable<(IMethodSymbol Symbol, AttributeData? AttrData)> GetAllMethodWithAttribute(this INamedTypeSymbol interfaceSymbol, string fullName, INamedTypeSymbol? classSymbol = null) + { + var all = interfaceSymbol.Interfaces.Insert(0, interfaceSymbol); + foreach (var m in all) { - var all = interfaceSymbol.Interfaces.Insert(0, interfaceSymbol); - foreach (var m in all) + foreach (var item in m.GetMembers().Where(m => m is IMethodSymbol).Cast()) { - foreach (var item in m.GetMembers().Where(m => m is IMethodSymbol).Cast()) + if (item.MethodKind == MethodKind.Constructor) { - if (item.MethodKind == MethodKind.Constructor) - { - continue; - } + continue; + } - var classMethod = classSymbol?.GetMembers().FirstOrDefault(m => m.Name == item.Name); + var classMethod = classSymbol?.GetMembers().FirstOrDefault(m => m.Name == item.Name); - if (!item.GetAttribute(fullName, out var a)) + if (!item.GetAttribute(fullName, out var a)) + { + if (!classMethod.GetAttribute(fullName, out a)) { - if (!classMethod.GetAttribute(fullName, out a)) - { - a = null; - } + a = null; } - var method = m.IsGenericType ? item.ConstructedFrom : item; - yield return (method, a); } + var method = m.IsGenericType ? item.ConstructedFrom : item; + yield return (method, a); } } + } - - - public static IEnumerable GetGenericTypes(this ITypeSymbol symbol) + public static ITypeSymbol GetElementType(this ITypeSymbol typeSymbol) + { + if (typeSymbol.HasInterfaceAll("System.Collections.IEnumerable") && typeSymbol.SpecialType == SpecialType.None) { - if (symbol is INamedTypeSymbol { IsGenericType: true, TypeArguments: var types }) + if (typeSymbol is IArrayTypeSymbol a) { - foreach (var t in types) - { - yield return t; - } + return a.ElementType; } - //else - //{ - // yield return symbol; - //} + return typeSymbol.GetGenericTypes().First(); } + return typeSymbol; + } - public static IEnumerable GetTypeParameters(this ISymbol symbol) + public static IEnumerable GetGenericTypes(this ITypeSymbol symbol) + { + if (symbol is INamedTypeSymbol { IsGenericType: true, TypeArguments: var types }) { - IEnumerable tpc = []; - if (symbol is IMethodSymbol method) - { - tpc = method.TypeParameters; - } - else if (symbol is INamedTypeSymbol typeSymbol) - { - tpc = typeSymbol.TypeParameters; - } - else + foreach (var t in types) { - yield break; + yield return t; } + } + //else + //{ + // yield return symbol; + //} + } - foreach (var tp in tpc) - { - List cs = tp.ConstraintTypes.Select(t => t.Name).ToList(); - tp.HasNotNullConstraint.IsTrueThen(() => cs.Add("notnull")); - tp.HasReferenceTypeConstraint.IsTrueThen(() => cs.Add("class")); - tp.HasUnmanagedTypeConstraint.IsTrueThen(() => cs.Add("unmanaged ")); - tp.HasValueTypeConstraint.IsTrueThen(() => cs.Add("struct")); - tp.HasConstructorConstraint.IsTrueThen(() => cs.Add("new()")); - yield return new(tp.Name, [.. cs]); - } + public static IEnumerable GetTypeParameters(this ISymbol symbol) + { + IEnumerable tpc = []; + if (symbol is IMethodSymbol method) + { + tpc = method.TypeParameters; + } + else if (symbol is INamedTypeSymbol typeSymbol) + { + tpc = typeSymbol.TypeParameters; + } + else + { + yield break; } - private static void IsTrueThen(this bool value, Action action) + foreach (var tp in tpc) { - if (value) - { - action(); - } + List cs = tp.ConstraintTypes.Select(t => t.Name).ToList(); + tp.HasNotNullConstraint.IsTrueThen(() => cs.Add("notnull")); + tp.HasReferenceTypeConstraint.IsTrueThen(() => cs.Add("class")); + tp.HasUnmanagedTypeConstraint.IsTrueThen(() => cs.Add("unmanaged ")); + tp.HasValueTypeConstraint.IsTrueThen(() => cs.Add("struct")); + tp.HasConstructorConstraint.IsTrueThen(() => cs.Add("new()")); + yield return new(tp.Name, [.. cs]); + } + } + + private static void IsTrueThen(this bool value, Action action) + { + if (value) + { + action(); } } } diff --git a/src/Generators.Shared/StringExtensions.cs b/src/Generators.Shared/StringExtensions.cs index 1748e12..f356718 100644 --- a/src/Generators.Shared/StringExtensions.cs +++ b/src/Generators.Shared/StringExtensions.cs @@ -1,10 +1,9 @@ -namespace Generators.Shared +namespace Generators.Shared; + +internal static class StringExtensions { - internal static class StringExtensions + public static bool IsNullOrEmpty(this string? value) { - public static bool IsNullOrEmpty(this string? value) - { - return string.IsNullOrEmpty(value); - } + return string.IsNullOrEmpty(value); } } \ No newline at end of file