Skip to content

Instantly share code, notes, and snippets.

@paxbun
Created September 12, 2022 10:52
Show Gist options
  • Save paxbun/bb3280e0004b0bf49d24e03ce713f5f7 to your computer and use it in GitHub Desktop.
Save paxbun/bb3280e0004b0bf49d24e03ce713f5f7 to your computer and use it in GitHub Desktop.
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
// ==== Foo ====
// Foo.Nullable: Nullable
// Foo.NonNullable: NotNull
PrintNullabilityState(typeof(Foo));
// ==== DynamicFoo ====
// DynamicFoo.Nullable: Nullable
// DynamicFoo.NonNullable: NotNull
PrintNullabilityState(GenerateNullableTypeDynamically());
void PrintNullabilityState(Type type)
{
NullabilityInfoContext context = new();
Console.WriteLine("==== {0} ====", type.FullName);
Console.WriteLine(
"{0}.Nullable: {1}\n{0}.NonNullable: {2}",
type.FullName,
context.Create(type.GetProperty("Nullable")!).ReadState,
context.Create(type.GetProperty("NonNullable")!).ReadState
);
Console.WriteLine();
}
// Wrapper of the following three functions.
Type GenerateNullableTypeDynamically()
{
AssemblyName assemblyName = new("MyAssembly");
AssemblyBuilder assemblyBuilder
= AssemblyBuilder.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.RunAndCollect);
ModuleBuilder moduleBuilder
= assemblyBuilder.DefineDynamicModule(assemblyName.Name!);
Type nullableContextAttribute
= CreateNullableContextAttribute(moduleBuilder);
Type nullableAttribute
= CreateNullableAttribute(moduleBuilder);
Type dynamicClass
= CreateDynamicClass(moduleBuilder, nullableContextAttribute, nullableAttribute, "DynamicFoo");
return dynamicClass;
}
// Defines System.Runtime.CompilerServices.NullableContextAttribute.
Type CreateNullableContextAttribute(ModuleBuilder moduleBuilder)
{
/*
* namespace System.Runtime.CompilerServices {
* internal sealed class NullableContextAttribute : Attribute {
* ...
* }
* }
*/
TypeBuilder nullableContextAttributeTypeBuilder
= moduleBuilder.DefineType(
"System.Runtime.CompilerServices.NullableContextAttribute",
TypeAttributes.Sealed,
typeof(Attribute));
/*
* namespace System.Runtime.CompilerServices {
* [System.Runtime.CompilerServices.CompilerGenerated]
* [System.AttributeUsageAttribute(
* System.AttributeTargets.Class
* | System.AttributeTargets.Struct
* | System.AttributeTargets.Method
* | System.AttributeTargets.Interface
* | System.AttributeTargets.Delegate
* )]
* internal sealed class NullableContextAttribute : Attribute {
* ...
* }
* }
*/
nullableContextAttributeTypeBuilder.SetCustomAttribute(
new CustomAttributeBuilder(
typeof(CompilerGeneratedAttribute).GetConstructor(Type.EmptyTypes)!,
Array.Empty<object?>()));
nullableContextAttributeTypeBuilder.SetCustomAttribute(
new CustomAttributeBuilder(
typeof(AttributeUsageAttribute).GetConstructor(new[] { typeof(AttributeTargets) })!,
new object[]
{
AttributeTargets.Class
| AttributeTargets.Struct
| AttributeTargets.Method
| AttributeTargets.Interface
| AttributeTargets.Delegate
}));
/*
* namespace System.Runtime.CompilerServices {
* ...
* internal sealed class NullableContextAttribute : Attribute {
* public readonly byte Flag;
* ...
* }
* }
*/
FieldBuilder fieldBuilder
= nullableContextAttributeTypeBuilder.DefineField(
"Flag",
typeof(byte),
FieldAttributes.Public | FieldAttributes.InitOnly);
/*
* namespace System.Runtime.CompilerServices {
* ...
* internal sealed class NullableContextAttribute : Attribute {
* public readonly byte Flag;
* public NullableContextAttribute(byte b): base() {
* Flag = b;
* }
* }
* }
*/
ConstructorInfo attributeConstructorWithoutParams
= typeof(Attribute).GetConstructor(
BindingFlags.NonPublic
| BindingFlags.Instance,
Type.EmptyTypes)!;
ConstructorBuilder constructorBuilder
= nullableContextAttributeTypeBuilder.DefineConstructor(
MethodAttributes.Public,
CallingConventions.Standard,
new[] { typeof(byte) });
// public NullableContextAttribute(byte b)
ILGenerator ilGenerator = constructorBuilder.GetILGenerator();
// : base() {
ilGenerator.Emit(OpCodes.Ldarg_0);
ilGenerator.Emit(OpCodes.Call, attributeConstructorWithoutParams);
// Flag = b;
ilGenerator.Emit(OpCodes.Ldarg_0);
ilGenerator.Emit(OpCodes.Ldarg_1);
ilGenerator.Emit(OpCodes.Stfld, fieldBuilder);
// return;
ilGenerator.Emit(OpCodes.Ret);
// }
return nullableContextAttributeTypeBuilder.CreateType()!;
}
// Defines System.Runtime.CompilerServices.NullableAttribute.
Type CreateNullableAttribute(ModuleBuilder moduleBuilder)
{
TypeBuilder nullableAttributeTypeBuilder
= moduleBuilder.DefineType(
"System.Runtime.CompilerServices.NullableAttribute",
TypeAttributes.Sealed,
typeof(Attribute));
nullableAttributeTypeBuilder.SetCustomAttribute(
new CustomAttributeBuilder(
typeof(CompilerGeneratedAttribute).GetConstructor(Type.EmptyTypes)!,
Array.Empty<object?>()));
nullableAttributeTypeBuilder.SetCustomAttribute(
new CustomAttributeBuilder(
typeof(AttributeUsageAttribute).GetConstructor(new[] { typeof(AttributeTargets) })!,
new object[]
{
AttributeTargets.Class| AttributeTargets.Property | AttributeTargets.Field |
AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue |
AttributeTargets.GenericParameter
}));
FieldBuilder fieldBuilder
= nullableAttributeTypeBuilder.DefineField(
"NullableFlags",
typeof(byte[]),
FieldAttributes.Public | FieldAttributes.InitOnly);
ConstructorBuilder constructorBuilder1
= nullableAttributeTypeBuilder.DefineConstructor(
MethodAttributes.Public,
CallingConventions.Standard,
new[] { typeof(byte) });
/*
* public NullableAttribute(byte b)
* {
* byte[] array = new byte[1];
* array[0] = b;
* this.NullableFlags = array;
* }
*/
ConstructorInfo attributeConstructorWithoutParams
= typeof(Attribute).GetConstructor(
BindingFlags.NonPublic | BindingFlags.Instance, Type.EmptyTypes)!;
ILGenerator ilGenerator1 = constructorBuilder1.GetILGenerator();
ilGenerator1.Emit(OpCodes.Ldarg_0);
ilGenerator1.Emit(OpCodes.Call, attributeConstructorWithoutParams);
ilGenerator1.Emit(OpCodes.Ldarg_0);
ilGenerator1.Emit(OpCodes.Ldc_I4_1);
ilGenerator1.Emit(OpCodes.Newarr, typeof(byte));
ilGenerator1.Emit(OpCodes.Dup);
ilGenerator1.Emit(OpCodes.Ldc_I4_0);
ilGenerator1.Emit(OpCodes.Ldarg_1);
ilGenerator1.Emit(OpCodes.Stelem_I1);
ilGenerator1.Emit(OpCodes.Stfld, fieldBuilder);
ilGenerator1.Emit(OpCodes.Ret);
ConstructorBuilder constructorBuilder2 = nullableAttributeTypeBuilder.DefineConstructor(
MethodAttributes.Public,
CallingConventions.Standard,
new[] { typeof(byte[]) }
);
/*
* public NullableAttribute(byte[] array)
* {
* this.NullableFlags = array;
* }
*/
ILGenerator ilGenerator2 = constructorBuilder2.GetILGenerator();
ilGenerator2.Emit(OpCodes.Ldarg_0);
ilGenerator2.Emit(OpCodes.Call, attributeConstructorWithoutParams);
ilGenerator2.Emit(OpCodes.Ldarg_0);
ilGenerator2.Emit(OpCodes.Ldarg_1);
ilGenerator2.Emit(OpCodes.Stfld, fieldBuilder);
ilGenerator2.Emit(OpCodes.Ret);
return nullableAttributeTypeBuilder.CreateType()!;
}
Type CreateDynamicClass(
ModuleBuilder moduleBuilder,
Type nullableContextAttribute,
Type nullableAttribute,
string className
)
{
/*
* abstract class <className> { ... }
*/
TypeBuilder typeBuilder
= moduleBuilder.DefineType(className, TypeAttributes.Abstract);
typeBuilder.SetCustomAttribute(
new CustomAttributeBuilder(
nullableContextAttribute.GetConstructor(new[] { typeof(byte) })!,
new object[] { (byte)1 }
)
);
/*
* abstract class <className> {
* ...
* public abstract string? Nullable { get; }
* ...
* }
*/
PropertyBuilder nullablePropertyBuilder
= typeBuilder.DefineProperty(
"Nullable", PropertyAttributes.None, typeof(string), null);
nullablePropertyBuilder.SetCustomAttribute(
new CustomAttributeBuilder(
nullableAttribute.GetConstructor(new[] { typeof(byte) })!,
new object[] { (byte)2 }));
MethodBuilder nullablePropertyGetterBuilder
= typeBuilder.DefineMethod(
"get_Nullable",
MethodAttributes.Public
| MethodAttributes.Abstract
| MethodAttributes.Virtual,
typeof(string),
null);
nullablePropertyGetterBuilder.SetCustomAttribute(
new CustomAttributeBuilder(
nullableContextAttribute.GetConstructor(new[] { typeof(byte) })!,
new object[] { (byte)2 }));
nullablePropertyBuilder.SetGetMethod(nullablePropertyGetterBuilder);
/*
* abstract class <className> {
* ...
* public abstract string NonNullable { get; }
* ...
* }
*/
PropertyBuilder nonNullablePropertyBuilder
= typeBuilder.DefineProperty(
"NonNullable", PropertyAttributes.None, typeof(string), null);
MethodBuilder nonNullablePropertyGetterBuilder
= typeBuilder.DefineMethod(
"get_NonNullable",
MethodAttributes.Public
| MethodAttributes.Abstract
| MethodAttributes.Virtual,
typeof(string),
null);
nonNullablePropertyBuilder.SetGetMethod(nonNullablePropertyGetterBuilder);
return typeBuilder.CreateType()!;
}
class Foo
{
public string? Nullable { get; set; } = null!;
public string NonNullable { get; set; } = null!;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment