既存のプログラムアセンブリからエンティティコードのコピーを作成する必要がある場合がありますが、これらのコードを手動でコピーするのは時間がかかる場合があります。このプロセスを簡素化するために、Roslyn
を使用してコードを自動生成できます。
この記事では、Roslyn
を使用してシンプルなクラスをコードに戻す方法について説明します。
実現方法
この機能を実現するには、次の手順が必要です。
-
クラス内のすべてのプロパティ型がサポートされている型リストに含まれているかをチェックします。
-
C#コード生成ユニットを作成し、必要な名前空間を追加します。
-
名前空間を作成し、クラス定義を追加します。
-
クラスのすべてのプロパティをクラス定義に追加します。
-
各プロパティに
get/set
メソッドを追加し、default
値を設定します。
コードの実装
サポートされる型をチェックします
PropertyInfo
配列から、サポートされるプロパティ型かどうかをチェックする静的メソッドです。まず、サポートされるプロパティ型のリストを定義し、その中にGUID
、DateTime
、string
、int
などが含まれます。
次に、プロパティ情報の配列を順番にチェックして、各プロパティの型が配列の場合は、配列の要素型を取得します。その後、サポートされるプロパティ型のリストに含まれている場合は、次のプロパティに進みます。サポートされていないプロパティ型が見つかった場合は、NotSupportedException
をスローします。
private static void CheckSamplePropertiesTypes(PropertyInfo[] propertyInfos)
{
var supportTypes = new[]
{
typeof(Guid),
typeof(DateTime),
typeof(string),
typeof(int),
typeof(char),
typeof(uint),
typeof(short),
typeof(ushort),
typeof(long),
typeof(ulong),
typeof(bool),
typeof(float),
typeof(double),
typeof(byte)
};
foreach (var p in propertyInfos)
{
var propertyType = p.PropertyType;
if (propertyType.BaseType == typeof(Array))
{
propertyType = propertyType.GetElementType();
}
if (supportTypes.Contains(propertyType))
{
continue;
}
throw new NotSupportedException();
}
}
プロパティを追加します
プロパティの名前と型から、プロパティ定義を作成します。その後、プロパティのget/set
アクセサを設定し、AddPropertyInitValue
メソッドを使用してプロパティの初期値を設定します。最後に、AddMembers
メソッドを使用して、クラス定義に新しいプロパティを追加します。
AddPropertyInitValue
メソッドは、プロパティの初期値を設定するために使用されます。まず、プロパティの型に応じて、初期値を設定するためのコードを生成します。例えば、プロパティの型がstring
の場合は、初期値に空文字列を設定します。また、プロパティの型が配列の場合は、要素の型から配列を生成し、初期値に設定します。
private static ClassDeclarationSyntax AddProperty(ClassDeclarationSyntax classDeclaration, PropertyInfo propertyInfo)
{
var propertyDeclaration = SyntaxFactory.PropertyDeclaration(
SyntaxFactory.ParseTypeName(propertyInfo.PropertyType.Name),
propertyInfo.Name)
.AddModifiers(SyntaxFactory.Token(SyntaxKind.PublicKeyword))
.AddAccessorListAccessors(
SyntaxFactory.AccessorDeclaration(SyntaxKind.GetAccessorDeclaration)
.WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken)),
SyntaxFactory.AccessorDeclaration(SyntaxKind.SetAccessorDeclaration)
.WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken)));
propertyDeclaration = AddPropertyInitValue(propertyDeclaration, propertyInfo);
return classDeclaration.AddMembers(propertyDeclaration);
}
private static PropertyDeclarationSyntax AddPropertyInitValue(
PropertyDeclarationSyntax propertyDeclaration,
PropertyInfo propertyInfo)
{
var propertyType = propertyInfo.PropertyType;
var newLine = Environment.NewLine;
if (propertyType == typeof(string))
{
propertyDeclaration = propertyDeclaration.WithInitializer(
SyntaxFactory.EqualsValueClause(
SyntaxFactory.LiteralExpression(
SyntaxKind.StringLiteralExpression,
SyntaxFactory.Literal(""))))
.WithSemicolonToken(
SyntaxFactory.Token(SyntaxKind.SemicolonToken))
.WithTrailingTrivia(SyntaxFactory.Comment(newLine));
}
else if (propertyType.BaseType == typeof(Array))
{
var elementType = propertyType.GetElementType()!;
propertyDeclaration = propertyDeclaration.WithInitializer(
SyntaxFactory.EqualsValueClause(
SyntaxFactory.ArrayCreationExpression(
SyntaxFactory
.ArrayType(SyntaxFactory.ParseTypeName(elementType.Name))
.WithRankSpecifiers(
SyntaxFactory.SingletonList(
SyntaxFactory.ArrayRankSpecifier(
SyntaxFactory.SingletonSeparatedList<ExpressionSyntax>(
SyntaxFactory.LiteralExpression(
SyntaxKind.NumericLiteralExpression,
SyntaxFactory.Literal(0)))))))))
.WithSemicolonToken(
SyntaxFactory.Token(SyntaxKind.SemicolonToken))
.WithTrailingTrivia(SyntaxFactory.Comment(newLine));
}
return propertyDeclaration;
}
クラスと名前空間を追加して組み立てます
この関数は、与えられたType
型のクラスの全てのプロパティが支援される型であることを確認し、C#構文ツリーを生成して返します。生成されたC#コードはstring
として返されます。
public static string GenerateSampleClass(
Type type,
string newClassNamespace,
Func<Type, PropertyInfo[]>? getPropertyInfosFunc = null)
{
getPropertyInfosFunc ??= t => t.GetProperties();
var allProperties = getPropertyInfosFunc.Invoke(type);
CheckSamplePropertiesTypes(allProperties);
var syntax = SyntaxFactory.CompilationUnit()
.AddUsings(SyntaxFactory.UsingDirective(SyntaxFactory.ParseName("System")))
.WithLeadingTrivia(SyntaxFactory.Comment("// ReSharper disable All"));
var @namespace = SyntaxFactory.NamespaceDeclaration(SyntaxFactory.ParseName(newClassNamespace))
.NormalizeWhitespace();
var classDeclaration = SyntaxFactory.ClassDeclaration(type.Name)
.AddModifiers(SyntaxFactory.Token(SyntaxKind.PublicKeyword));
foreach (var p in allProperties)
{
classDeclaration = AddProperty(classDeclaration, p);
}
@namespace = @namespace.AddMembers(classDeclaration);
syntax = syntax.AddMembers(@namespace);
return syntax.NormalizeWhitespace().ToFullString();
}
全部のコード
- CodeGenerateTools.cs
public static class CodeGenerateTools
{
private static void CheckSamplePropertiesTypes(PropertyInfo[] propertyInfos)
{
var supportTypes = new[]
{
typeof(Guid),
typeof(DateTime),
typeof(string),
typeof(int),
typeof(char),
typeof(uint),
typeof(short),
typeof(ushort),
typeof(long),
typeof(ulong),
typeof(bool),
typeof(float),
typeof(double),
typeof(byte)
};
foreach (var p in propertyInfos)
{
var propertyType = p.PropertyType;
if (propertyType.BaseType == typeof(Array))
{
propertyType = propertyType.GetElementType();
}
if (supportTypes.Contains(propertyType))
{
continue;
}
throw new NotSupportedException();
}
}
public static string GenerateSampleClass(
Type type,
string newClassNamespace,
Func<Type, PropertyInfo[]>? getPropertyInfosFunc = null)
{
getPropertyInfosFunc ??= t => t.GetProperties();
var allProperties = getPropertyInfosFunc.Invoke(type);
CheckSamplePropertiesTypes(allProperties);
var syntax = SyntaxFactory.CompilationUnit()
.AddUsings(SyntaxFactory.UsingDirective(SyntaxFactory.ParseName("System")))
.WithLeadingTrivia(SyntaxFactory.Comment("// ReSharper disable All"));
var @namespace = SyntaxFactory.NamespaceDeclaration(SyntaxFactory.ParseName(newClassNamespace))
.NormalizeWhitespace();
var classDeclaration = SyntaxFactory.ClassDeclaration(type.Name)
.AddModifiers(SyntaxFactory.Token(SyntaxKind.PublicKeyword));
foreach (var p in allProperties)
{
classDeclaration = AddProperty(classDeclaration, p);
}
@namespace = @namespace.AddMembers(classDeclaration);
syntax = syntax.AddMembers(@namespace);
return syntax.NormalizeWhitespace().ToFullString();
}
private static ClassDeclarationSyntax AddProperty(ClassDeclarationSyntax classDeclaration, PropertyInfo propertyInfo)
{
var propertyDeclaration = SyntaxFactory.PropertyDeclaration(
SyntaxFactory.ParseTypeName(propertyInfo.PropertyType.Name),
propertyInfo.Name)
.AddModifiers(SyntaxFactory.Token(SyntaxKind.PublicKeyword))
.AddAccessorListAccessors(
SyntaxFactory.AccessorDeclaration(SyntaxKind.GetAccessorDeclaration)
.WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken)),
SyntaxFactory.AccessorDeclaration(SyntaxKind.SetAccessorDeclaration)
.WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken)));
propertyDeclaration = AddPropertyInitValue(propertyDeclaration, propertyInfo);
return classDeclaration.AddMembers(propertyDeclaration);
}
private static PropertyDeclarationSyntax AddPropertyInitValue(
PropertyDeclarationSyntax propertyDeclaration,
PropertyInfo propertyInfo)
{
var propertyType = propertyInfo.PropertyType;
var newLine = Environment.NewLine;
if (propertyType == typeof(string))
{
propertyDeclaration = propertyDeclaration.WithInitializer(
SyntaxFactory.EqualsValueClause(
SyntaxFactory.LiteralExpression(
SyntaxKind.StringLiteralExpression,
SyntaxFactory.Literal(""))))
.WithSemicolonToken(
SyntaxFactory.Token(SyntaxKind.SemicolonToken))
.WithTrailingTrivia(SyntaxFactory.Comment(newLine));
}
else if (propertyType.BaseType == typeof(Array))
{
var elementType = propertyType.GetElementType()!;
propertyDeclaration = propertyDeclaration.WithInitializer(
SyntaxFactory.EqualsValueClause(
SyntaxFactory.ArrayCreationExpression(
SyntaxFactory
.ArrayType(SyntaxFactory.ParseTypeName(elementType.Name))
.WithRankSpecifiers(
SyntaxFactory.SingletonList(
SyntaxFactory.ArrayRankSpecifier(
SyntaxFactory.SingletonSeparatedList<ExpressionSyntax>(
SyntaxFactory.LiteralExpression(
SyntaxKind.NumericLiteralExpression,
SyntaxFactory.Literal(0)))))))))
.WithSemicolonToken(
SyntaxFactory.Token(SyntaxKind.SemicolonToken))
.WithTrailingTrivia(SyntaxFactory.Comment(newLine));
}
return propertyDeclaration;
}
}