Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
CSharpNotesForProfessionals.pdf
Скачиваний:
57
Добавлен:
20.05.2023
Размер:
6.12 Mб
Скачать

Chapter 63: Reflection

Reflection is a C# language mechanism for accessing dynamic object properties on runtime. Typically, reflection is used to fetch the information about dynamic object type and object attribute values. In REST application, for example, reflection could be used to iterate through serialized response object.

Remark: According to MS guidelines performance critical code should avoid reflection. See https://msdn.microsoft.com/en-us/library/ 647790.aspx

Section 63.1: Get the members of a type

using System;

using System.Reflection; using System.Linq;

public class Program

{

public static void Main()

{

var members = typeof(object)

.GetMembers(BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance);

foreach (var member in members)

{

bool inherited = member.DeclaringType.Equals( typeof(object).Name ); Console.WriteLine($"{member.Name} is a {member.MemberType}, " +

$"it has {(inherited ? "":"not")} been inherited.");

}

}

}

Output (see note about output order further down):

GetType is a Method, it has not been inherited. GetHashCode is a Method, it has not been inherited. ToString is a Method, it has not been inherited. Equals is a Method, it has not been inherited. Equals is a Method, it has not been inherited. ReferenceEquals is a Method, it has not been inherited. .ctor is a Constructor, it has not been inherited.

We can also use the GetMembers() without passing any BindingFlags. This will return all public members of that specific type.

One thing to note that GetMembers does not return the members in any particular order, so never rely on the order that GetMembers returns you.

View Demo

Section 63.2: Get a method and invoke it

Get Instance method and invoke it

using System;

public class Program

{

public static void Main()

GoalKicker.com – C# Notes for Professionals

332

{

var theString = "hello"; var method = theString

.GetType()

.GetMethod("Substring",

new[] {typeof(int), typeof(int)}); //The types of the method

arguments

var result = method.Invoke(theString, new object[] {0, 4}); Console.WriteLine(result);

}

}

Output:

hell

View Demo

Get Static method and invoke it

On the other hand, if the method is static, you do not need an instance to call it.

var method = typeof(Math).GetMethod("Exp");

var result = method.Invoke(null, new object[] {2});//Pass null as the first argument (no need for an instance)

Console.WriteLine(result); //You'll get e^2

Output:

7.38905609893065

View Demo

Section 63.3: Creating an instance of a Type

The simplest way is to use the Activator class.

However, even though Activator performance have been improved since .NET 3.5, using

Activator.CreateInstance() is bad option sometimes, due to (relatively) low performance: Test 1, Test 2, Test 3...

With Activator class

Type type = typeof(BigInteger);

object result = Activator.CreateInstance(type); //Requires parameterless constructor. Console.WriteLine(result); //Output: 0

result = Activator.CreateInstance(type, 123); //Requires a constructor which can receive an 'int' compatible argument.

Console.WriteLine(result); //Output: 123

You can pass an object array to Activator.CreateInstance if you have more than one parameter.

// With a constructor such as MyClass(int, int, string)

Activator.CreateInstance(typeof(MyClass), new object[] { 1, 2, "Hello World" });

GoalKicker.com – C# Notes for Professionals

333

Type type = typeof(someObject);

var instance = Activator.CreateInstance(type);

For a generic type

The MakeGenericType method turns an open generic type (like List<>) into a concrete type (like List<string>) by applying type arguments to it.

//generic List with no parameters

Type openType = typeof(List<>);

//To create a List<string>

Type[] tArgs = { typeof(string) };

Type target = openType.MakeGenericType(tArgs);

//Create an instance - Activator.CreateInstance will call the default constructor.

//This is equivalent to calling new List<string>().

List<string> result = (List<string>)Activator.CreateInstance(target);

The List<> syntax is not permitted outside of a typeof expression.

Without Activator class

Using new keyword (will do for parameterless constructors)

T GetInstance<T>() where T : new()

{

T instance = new T(); return instance;

}

Using Invoke method

//Get the instance of the desired constructor (here it takes a string as a parameter).

ConstructorInfo c = typeof(T).GetConstructor(new[] { typeof(string) });

//Don't forget to check if such constructor exists

if (c == null)

throw new InvalidOperationException(string.Format("A constructor for type '{0}' was not found.", typeof(T)));

T instance = (T)c.Invoke(new object[] { "test" });

Using Expression trees

Expression trees represent code in a tree-like data structure, where each node is an expression. As MSDN explains:

Expression is a sequence of one or more operands and zero or more operators that can be evaluated to a single value, object, method, or namespace. Expressions can consist of a literal value, a method invocation, an operator and its operands, or a simple name. Simple names can be the name of a variable, type member, method parameter, namespace or type.

public class GenericFactory<TKey, TType>

{

private readonly Dictionary<TKey, Func<object[], TType>> _registeredTypes; // dictionary, that holds constructor functions.

private object _locker = new object(); // object for locking dictionary, to guarantee thread

GoalKicker.com – C# Notes for Professionals

334

safety

public GenericFactory()

{

_registeredTypes = new Dictionary<TKey, Func<object[], TType>>();

}

///<summary>

///Find and register suitable constructor for type

///</summary>

///<typeparam name="TType"></typeparam>

///<param name="key">Key for this constructor</param>

///<param name="parameters">Parameters</param>

public void Register(TKey key, params Type[] parameters)

{

ConstructorInfo ci = typeof(TType).GetConstructor(BindingFlags.Public | BindingFlags.Instance, null, CallingConventions.HasThis, parameters, new ParameterModifier[] { });

// Get the instance of ctor. if (ci == null)

throw new InvalidOperationException(string.Format("Constructor for type '{0}' was not found.", typeof(TType)));

Func<object[], TType> ctor;

lock (_locker)

{

if (!_registeredTypes.TryGetValue(key, out ctor)) // check if such ctor already

been registered

{

var pExp = Expression.Parameter(typeof(object[]), "arguments"); // create

parameter Expression

var ctorParams = ci.GetParameters(); // get parameter info from constructor

var argExpressions = new Expression[ctorParams.Length]; // array that will contains parameter expessions

for (var i = 0; i < parameters.Length; i++)

{

var indexedAcccess = Expression.ArrayIndex(pExp, Expression.Constant(i));

if (!parameters[i].IsClass && !parameters[i].IsInterface) // check if parameter is a value type

{

var localVariable = Expression.Variable(parameters[i], "localVariable"); // if so - we should create local variable that will store paraameter value

var block = Expression.Block(new[] { localVariable }, Expression.IfThenElse(Expression.Equal(indexedAcccess,

Expression.Constant(null)),

Expression.Assign(localVariable,

Expression.Default(parameters[i])),

Expression.Assign(localVariable, Expression.Convert(indexedAcccess, parameters[i]))

), localVariable

);

argExpressions[i] = block;

}

else

argExpressions[i] = Expression.Convert(indexedAcccess, parameters[i]);

GoalKicker.com – C# Notes for Professionals

335