|
1 | 1 | // Licensed to the .NET Foundation under one or more agreements. |
2 | 2 | // The .NET Foundation licenses this file to you under the MIT license. |
3 | 3 | // Runnable examples for DynamicMethod XML doc comments. |
4 | | -// Each #region is referenced by <code> tags in DynamicMethod.cs. |
| 4 | +// Each static method is a self-contained sample referenced by <code> tags in DynamicMethod.cs. |
5 | 5 | // Run: dotnet run DynamicMethod.Examples.cs |
6 | 6 |
|
7 | 7 | using System.Globalization; |
8 | 8 | using System.Reflection; |
9 | 9 | using System.Reflection.Emit; |
10 | 10 |
|
11 | | -// Create a DynamicMethod shared by the property/method examples below. |
12 | | -DynamicMethod hello = new("Hello", typeof(int), [typeof(string), typeof(int)], typeof(string).Module); |
13 | | -int failures = 0; |
| 11 | +DynamicMethodExamples.MetadataAndProperties(); |
| 12 | +DynamicMethodExamples.DefineParameterAndGetParameters(); |
| 13 | +DynamicMethodExamples.GetILGeneratorAndInvoke(); |
14 | 14 |
|
15 | | -void Fail(string name, string message) |
16 | | -{ |
17 | | - Console.Error.WriteLine($"FAIL: {name} — {message}"); |
18 | | - failures++; |
19 | | -} |
| 15 | +Console.WriteLine("All examples passed."); |
| 16 | +return 0; |
20 | 17 |
|
21 | | -#region Name |
22 | | -// The name specified when the dynamic method was created. |
23 | | -Console.WriteLine($"Name: {hello.Name}"); |
24 | | -#endregion |
25 | | -if (hello.Name != "Hello") Fail("Name", $"Expected 'Hello', got '{hello.Name}'"); |
26 | | - |
27 | | -#region DeclaringType |
28 | | -// DeclaringType is always null for dynamic methods. |
29 | | -Console.WriteLine($"DeclaringType: {hello.DeclaringType?.ToString() ?? "(null)"}"); |
30 | | -#endregion |
31 | | -if (hello.DeclaringType is not null) Fail("DeclaringType", "Expected null"); |
32 | | - |
33 | | -#region ReflectedType |
34 | | -// ReflectedType is always null for dynamic methods. |
35 | | -Console.WriteLine($"ReflectedType: {hello.ReflectedType?.ToString() ?? "(null)"}"); |
36 | | -#endregion |
37 | | -if (hello.ReflectedType is not null) Fail("ReflectedType", "Expected null"); |
38 | | - |
39 | | -#region Module |
40 | | -// The module the dynamic method is associated with. |
41 | | -Console.WriteLine($"Module: {hello.Module}"); |
42 | | -#endregion |
43 | | -if (hello.Module != typeof(string).Module) Fail("Module", "Expected System.Private.CoreLib module"); |
44 | | - |
45 | | -#region Attributes |
46 | | -// Dynamic methods always have Public | Static attributes. |
47 | | -Console.WriteLine($"Attributes: {hello.Attributes}"); |
48 | | -#endregion |
49 | | -if (hello.Attributes != (MethodAttributes.Public | MethodAttributes.Static)) |
50 | | - Fail("Attributes", $"Expected Public | Static, got {hello.Attributes}"); |
51 | | - |
52 | | -#region CallingConvention |
53 | | -// Dynamic methods always use Standard calling convention. |
54 | | -Console.WriteLine($"CallingConvention: {hello.CallingConvention}"); |
55 | | -#endregion |
56 | | -if (hello.CallingConvention != CallingConventions.Standard) |
57 | | - Fail("CallingConvention", $"Expected Standard, got {hello.CallingConvention}"); |
58 | | - |
59 | | -#region InitLocals |
60 | | -// InitLocals defaults to true — local variables are zero-initialized. |
61 | | -Console.WriteLine($"InitLocals: {hello.InitLocals}"); |
62 | | -#endregion |
63 | | -if (!hello.InitLocals) Fail("InitLocals", "Expected true"); |
64 | | - |
65 | | -#region ReturnType |
66 | | -// The return type specified when the dynamic method was created. |
67 | | -Console.WriteLine($"ReturnType: {hello.ReturnType}"); |
68 | | -#endregion |
69 | | -if (hello.ReturnType != typeof(int)) Fail("ReturnType", $"Expected Int32, got {hello.ReturnType}"); |
70 | | - |
71 | | -#region ReturnTypeCustomAttributes |
72 | | -// At present there is no way to set custom attributes on the return type, |
73 | | -// so the list is always empty. |
74 | | -ICustomAttributeProvider caProvider = hello.ReturnTypeCustomAttributes; |
75 | | -object[] returnAttributes = caProvider.GetCustomAttributes(true); |
76 | | -Console.WriteLine($"Return type custom attributes: {returnAttributes.Length}"); |
77 | | -#endregion |
78 | | -if (returnAttributes.Length != 0) Fail("ReturnTypeCustomAttributes", "Expected 0 attributes"); |
79 | | - |
80 | | -#region DefineParameter |
81 | | -// Optionally add parameter metadata (useful for debugging). |
82 | | -hello.DefineParameter(1, ParameterAttributes.In, "message"); |
83 | | -hello.DefineParameter(2, ParameterAttributes.In, "valueToReturn"); |
84 | | -#endregion |
85 | | - |
86 | | -#region GetParameters |
87 | | -// Retrieve parameter info after calling DefineParameter. |
88 | | -ParameterInfo[] parameters = hello.GetParameters(); |
89 | | -foreach (ParameterInfo p in parameters) |
90 | | - Console.WriteLine($" Param: {p.Name}, {p.ParameterType}, {p.Attributes}"); |
91 | | -#endregion |
92 | | -if (parameters.Length != 2) Fail("GetParameters", $"Expected 2 parameters, got {parameters.Length}"); |
93 | | - |
94 | | -#region ToString |
95 | | -// ToString returns the signature: return type, name, and parameter types. |
96 | | -string sig = hello.ToString(); |
97 | | -Console.WriteLine($"ToString: {sig}"); |
98 | | -#endregion |
99 | | -if (!sig.Contains("Hello")) Fail("ToString", $"Expected signature to contain 'Hello'"); |
100 | | - |
101 | | -// Emit a method body so we can test CreateDelegate and Invoke. |
102 | | -MethodInfo writeString = typeof(Console).GetMethod("WriteLine", [typeof(string)])!; |
103 | | - |
104 | | -#region GetILGenerator |
105 | | -// Get an ILGenerator to emit the method body. |
106 | | -ILGenerator il = hello.GetILGenerator(); |
107 | | -il.Emit(OpCodes.Ldarg_0); |
108 | | -il.EmitCall(OpCodes.Call, writeString, null); |
109 | | -il.Emit(OpCodes.Ldarg_1); |
110 | | -il.Emit(OpCodes.Ret); |
111 | | -#endregion |
112 | | - |
113 | | -#region CreateDelegate |
114 | | -// Create a strongly-typed delegate for the dynamic method. |
115 | | -Func<string, int, int> hi = hello.CreateDelegate<Func<string, int, int>>(); |
116 | | -int retval = hi("Hello from delegate!", 42); |
117 | | -Console.WriteLine($"Delegate returned: {retval}"); |
118 | | -#endregion |
119 | | -if (retval != 42) Fail("CreateDelegate", $"Expected 42, got {retval}"); |
120 | | - |
121 | | -#region Invoke |
122 | | -// Invoke the dynamic method via reflection (slower than a delegate). |
123 | | -object? objRet = hello.Invoke(null, BindingFlags.ExactBinding, null, |
124 | | - ["Hello from Invoke!", 99], CultureInfo.InvariantCulture); |
125 | | -Console.WriteLine($"Invoke returned: {objRet}"); |
126 | | -#endregion |
127 | | -if (objRet is not 99) Fail("Invoke", $"Expected 99, got {objRet}"); |
128 | | - |
129 | | -if (failures > 0) |
| 18 | +class DynamicMethodExamples |
130 | 19 | { |
131 | | - Console.Error.WriteLine($"\n{failures} example(s) failed."); |
132 | | - return 1; |
| 20 | + /// <summary> |
| 21 | + /// Shows the metadata properties of a DynamicMethod: Name, DeclaringType, ReflectedType, |
| 22 | + /// Module, Attributes, CallingConvention, ReturnType, ReturnTypeCustomAttributes, InitLocals, |
| 23 | + /// and ToString. |
| 24 | + /// </summary> |
| 25 | + public static void MetadataAndProperties() |
| 26 | + { |
| 27 | + // Create a dynamic method associated with a module. |
| 28 | + DynamicMethod hello = new("Hello", typeof(int), [typeof(string), typeof(int)], typeof(string).Module); |
| 29 | + |
| 30 | + // Name: the name specified at creation. |
| 31 | + Console.WriteLine($"Name: {hello.Name}"); |
| 32 | + |
| 33 | + // DeclaringType is always null for dynamic methods. |
| 34 | + Console.WriteLine($"DeclaringType: {hello.DeclaringType?.ToString() ?? "(null)"}"); |
| 35 | + |
| 36 | + // ReflectedType is always null for dynamic methods. |
| 37 | + Console.WriteLine($"ReflectedType: {hello.ReflectedType?.ToString() ?? "(null)"}"); |
| 38 | + |
| 39 | + // Module: the module the dynamic method is associated with. |
| 40 | + Console.WriteLine($"Module: {hello.Module}"); |
| 41 | + |
| 42 | + // Attributes are always Public | Static. |
| 43 | + Console.WriteLine($"Attributes: {hello.Attributes}"); |
| 44 | + |
| 45 | + // CallingConvention is always Standard. |
| 46 | + Console.WriteLine($"CallingConvention: {hello.CallingConvention}"); |
| 47 | + |
| 48 | + // ReturnType: the return type specified at creation. |
| 49 | + Console.WriteLine($"ReturnType: {hello.ReturnType}"); |
| 50 | + |
| 51 | + // ReturnTypeCustomAttributes: no way to set custom attributes on the return type. |
| 52 | + ICustomAttributeProvider caProvider = hello.ReturnTypeCustomAttributes; |
| 53 | + object[] returnAttributes = caProvider.GetCustomAttributes(true); |
| 54 | + Console.WriteLine($"Return type custom attributes: {returnAttributes.Length}"); |
| 55 | + |
| 56 | + // InitLocals defaults to true — local variables are zero-initialized. |
| 57 | + Console.WriteLine($"InitLocals: {hello.InitLocals}"); |
| 58 | + |
| 59 | + // ToString returns the method signature (return type, name, parameter types). |
| 60 | + Console.WriteLine($"ToString: {hello.ToString()}"); |
| 61 | + } |
| 62 | + |
| 63 | + /// <summary> |
| 64 | + /// Shows DefineParameter and GetParameters: adding parameter metadata and retrieving it. |
| 65 | + /// </summary> |
| 66 | + public static void DefineParameterAndGetParameters() |
| 67 | + { |
| 68 | + DynamicMethod hello = new("Hello", typeof(int), [typeof(string), typeof(int)], typeof(string).Module); |
| 69 | + |
| 70 | + // DefineParameter adds metadata such as name and attributes. |
| 71 | + // Parameter positions are 1-based; position 0 refers to the return value. |
| 72 | + hello.DefineParameter(1, ParameterAttributes.In, "message"); |
| 73 | + hello.DefineParameter(2, ParameterAttributes.In, "valueToReturn"); |
| 74 | + |
| 75 | + // GetParameters retrieves the parameter info. |
| 76 | + ParameterInfo[] parameters = hello.GetParameters(); |
| 77 | + foreach (ParameterInfo p in parameters) |
| 78 | + Console.WriteLine($" Param: {p.Name}, {p.ParameterType}, {p.Attributes}"); |
| 79 | + } |
| 80 | + |
| 81 | + /// <summary> |
| 82 | + /// Shows GetILGenerator, CreateDelegate, and Invoke: emitting a method body and calling it. |
| 83 | + /// </summary> |
| 84 | + public static void GetILGeneratorAndInvoke() |
| 85 | + { |
| 86 | + DynamicMethod hello = new("Hello", typeof(int), [typeof(string), typeof(int)], typeof(string).Module); |
| 87 | + MethodInfo writeString = typeof(Console).GetMethod("WriteLine", [typeof(string)])!; |
| 88 | + |
| 89 | + // GetILGenerator returns an ILGenerator for emitting the method body. |
| 90 | + ILGenerator il = hello.GetILGenerator(); |
| 91 | + il.Emit(OpCodes.Ldarg_0); // push first arg (string) |
| 92 | + il.EmitCall(OpCodes.Call, writeString, null); // Console.WriteLine(string) |
| 93 | + il.Emit(OpCodes.Ldarg_1); // push second arg (int) |
| 94 | + il.Emit(OpCodes.Ret); // return it |
| 95 | + |
| 96 | + // CreateDelegate produces a strongly-typed delegate for the dynamic method. |
| 97 | + Func<string, int, int> hi = hello.CreateDelegate<Func<string, int, int>>(); |
| 98 | + int retval = hi("Hello from delegate!", 42); |
| 99 | + Console.WriteLine($"Delegate returned: {retval}"); |
| 100 | + |
| 101 | + // Invoke calls the dynamic method via reflection (slower than a delegate). |
| 102 | + object? objRet = hello.Invoke(null, BindingFlags.ExactBinding, null, |
| 103 | + ["Hello from Invoke!", 99], CultureInfo.InvariantCulture); |
| 104 | + Console.WriteLine($"Invoke returned: {objRet}"); |
| 105 | + } |
133 | 106 | } |
134 | | - |
135 | | -Console.WriteLine("\nAll examples passed."); |
136 | | -return 0; |
0 commit comments