Zoeken…


Invoering

Reflectie is een C # -taalmechanisme voor toegang tot dynamische objecteigenschappen tijdens runtime. Doorgaans wordt reflectie gebruikt om informatie over het dynamische objecttype en objectkenmerkwaarden op te halen. In de REST-toepassing kan bijvoorbeeld reflectie worden gebruikt om door een geserialiseerd responsobject te itereren.

Opmerking: Volgens MS-richtlijnen moet prestatie-kritische code reflectie voorkomen. Zie https://msdn.microsoft.com/en-us/library/ff647790.aspx

Opmerkingen

Reflectie geeft code toegang tot informatie over de assemblages, modules en types tijdens de uitvoering (uitvoering van het programma). Dit kan vervolgens verder worden gebruikt om typen dynamisch te maken, te wijzigen of te openen. Typen zijn eigenschappen, methoden, velden en attributen.

Verder lezen:

Reflection (C #)

Reflectie in .Net Framework

Download een System.Type

Voor een voorbeeld van een type:

var theString = "hello";
var theType = theString.GetType();

Van het type zelf:

var theType = typeof(string);

Krijg de leden van een 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.");
    }
  }
}

Uitvoer ( zie opmerking over uitvoervolgorde verderop ):

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 kunnen ook GetMembers() zonder BindingFlags te passeren. Hiermee worden alle openbare leden van dat specifieke type geretourneerd.

Een ding om op te merken dat GetMembers de leden niet in een bepaalde volgorde retourneert, vertrouw dus nooit op de bestelling die GetMembers retourneert.

Demo bekijken

Zoek een methode en gebruik deze

Download de Instance-methode en gebruik deze

using System;
                
public class Program
{
    public static void Main()
    {
        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:

hel

Demo bekijken

Download de statische methode en gebruik deze

Aan de andere kant, als de methode statisch is, hebt u geen instantie nodig om deze aan te roepen.

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

Demo bekijken

Eigenschappen ophalen en instellen

Basis gebruik:

PropertyInfo prop = myInstance.GetType().GetProperty("myProperty");
// get the value myInstance.myProperty
object value = prop.GetValue(myInstance);

int newValue = 1;
// set the value myInstance.myProperty to newValue
prop.setValue(myInstance, newValue);

Het instellen van alleen-lezen automatisch geïmplementeerde eigenschappen kan worden gedaan via het achtergrondveld (in .NET Framework is de naam van het achtergrondveld "k__BackingField"):

// get backing field info
FieldInfo fieldInfo = myInstance.GetType()
    .GetField("<myProperty>k__BackingField", BindingFlags.Instance | BindingFlags.NonPublic);

int newValue = 1;
// set the value of myInstance.myProperty backing field to newValue
fieldInfo.SetValue(myInstance, newValue);

Aangepaste attributen

Zoek eigenschappen met een aangepast kenmerk - MyAttribute

var props = t.GetProperties(BindingFlags.NonPublic | BindingFlags.Public | 
            BindingFlags.Instance).Where(
            prop => Attribute.IsDefined(prop, typeof(MyAttribute)));

Zoek alle aangepaste kenmerken voor een bepaalde eigenschap

var attributes = typeof(t).GetProperty("Name").GetCustomAttributes(false);

Noem alle klassen met aangepast kenmerk - MyAttribute

static IEnumerable<Type> GetTypesWithAttribute(Assembly assembly) {
    foreach(Type type in assembly.GetTypes()) {
        if (type.GetCustomAttributes(typeof(MyAttribute), true).Length > 0) {
            yield return type;
        }
    }
}

Lees de waarde van een aangepast kenmerk tijdens runtime

public static class AttributeExtensions
{

        /// <summary>
        /// Returns the value of a member attribute for any member in a class.
        ///     (a member is a Field, Property, Method, etc...)    
        /// <remarks>
        /// If there is more than one member of the same name in the class, it will return the first one (this applies to overloaded methods)
        /// </remarks>
        /// <example>
        /// Read System.ComponentModel Description Attribute from method 'MyMethodName' in class 'MyClass': 
        ///     var Attribute = typeof(MyClass).GetAttribute("MyMethodName", (DescriptionAttribute d) => d.Description);
        /// </example>
        /// <param name="type">The class that contains the member as a type</param>
        /// <param name="MemberName">Name of the member in the class</param>
        /// <param name="valueSelector">Attribute type and property to get (will return first instance if there are multiple attributes of the same type)</param>
        /// <param name="inherit">true to search this member's inheritance chain to find the attributes; otherwise, false. This parameter is ignored for properties and events</param>
        /// </summary>    
        public static TValue GetAttribute<TAttribute, TValue>(this Type type, string MemberName, Func<TAttribute, TValue> valueSelector, bool inherit = false) where TAttribute : Attribute
        {
            var att = type.GetMember(MemberName).FirstOrDefault().GetCustomAttributes(typeof(TAttribute), inherit).FirstOrDefault() as TAttribute;
            if (att != null)
            {
                return valueSelector(att);
            }
            return default(TValue);
        }
    }

Gebruik

//Read System.ComponentModel Description Attribute from method 'MyMethodName' in class 'MyClass'
var Attribute = typeof(MyClass).GetAttribute("MyMethodName", (DescriptionAttribute d) => d.Description);

Alle eigenschappen van een klasse doorlopen

Type type = obj.GetType();
//To restrict return properties. If all properties are required don't provide flag.
BindingFlags flags = BindingFlags.Public | BindingFlags.Instance; 
PropertyInfo[] properties = type.GetProperties(flags);

foreach (PropertyInfo property in properties)
{
    Console.WriteLine("Name: " + property.Name + ", Value: " + property.GetValue(obj, null));
}

Bepaling van generieke argumenten van instanties van generieke typen

Als u een exemplaar van een generiek type hebt, maar om een of andere reden het specifieke type niet kent, wilt u misschien de generieke argumenten bepalen die zijn gebruikt om dit exemplaar te maken.

Laten we zeggen dat iemand zo een instantie van List<T> heeft gemaakt en deze doorgeeft aan een methode:

var myList = new List<int>();
ShowGenericArguments(myList);

waar ShowGenericArguments deze handtekening heeft:

public void ShowGenericArguments(object o)

dus tijdens het compileren heb je geen idee welke generieke argumenten zijn gebruikt om o te creëren. Reflectie biedt veel methoden om generieke typen te inspecteren. Eerst kunnen we bepalen of het type o überhaupt een generiek type is:

public void ShowGenericArguments(object o)
{
    if (o == null) return;

    Type t = o.GetType();
    if (!t.IsGenericType) return;
    ...

Type.IsGenericType retourneert true als het type een generiek type is en false als dit niet het geval is.

Maar dit is niet alles wat we willen weten. List<> zelf is ook een generiek type. Maar we willen alleen voorbeelden van specifiek geconstrueerde generieke typen onderzoeken. Een geconstrueerd generiek type is bijvoorbeeld een List<int> met een specifiek typeargument voor alle generieke parameters .

De klasse Type biedt nog twee eigenschappen, IsConstructedGenericType en IsGenericTypeDefinition , om deze geconstrueerde generieke typen te onderscheiden van generieke IsGenericTypeDefinition :

typeof(List<>).IsGenericType // true
typeof(List<>).IsGenericTypeDefinition // true
typeof(List<>).IsConstructedGenericType// false

typeof(List<int>).IsGenericType // true
typeof(List<int>).IsGenericTypeDefinition // false
typeof(List<int>).IsConstructedGenericType// true

Om de generieke argumenten van een instantie op te sommen, kunnen we de methode GetGenericArguments() gebruiken die een Type array teruggeeft met de generieke type-argumenten:

public void ShowGenericArguments(object o)
{
    if (o == null) return;   
    Type t = o.GetType();
    if (!t.IsConstructedGenericType) return;

    foreach(Type genericTypeArgument in t.GetGenericArguments())
        Console.WriteLine(genericTypeArgument.Name);
}

Dus de aanroep van hierboven ( ShowGenericArguments(myList) ) resulteert in deze uitvoer:

Int32

Ontvang een generieke methode en gebruik deze

Laten we zeggen dat je klasse hebt met generieke methoden. En je moet zijn functies met reflectie noemen.

public class Sample
{
    public void GenericMethod<T>()
    {
        // ...
    }

    public static void StaticMethod<T>()
    {
        //...
    }
}

Laten we zeggen dat we de GenericMethod met type string willen aanroepen.

Sample sample = new Sample();//or you can get an instance via reflection

MethodInfo method = typeof(Sample).GetMethod("GenericMethod");
MethodInfo generic = method.MakeGenericMethod(typeof(string));
generic.Invoke(sample, null);//Since there are no arguments, we are passing null

Voor de statische methode heeft u geen instantie nodig. Daarom is het eerste argument ook nul.

MethodInfo method = typeof(Sample).GetMethod("StaticMethod");
MethodInfo generic = method.MakeGenericMethod(typeof(string));
generic.Invoke(null, null);

Maak een instantie van een generiek type en roep de methode aan

var baseType = typeof(List<>);
var genericType = baseType.MakeGenericType(typeof(String));
var instance = Activator.CreateInstance(genericType);
var method = genericType.GetMethod("GetHashCode");
var result = method.Invoke(instance, new object[] { });

Instanties starten die een interface implementeren (bijv. Activering van plug-ins)

Als u wilt dat uw aanvraag om ondersteuning van een plug-in systeem, bijvoorbeeld om de belasting plugins van de samenvoegingen gevestigd in plugins map:

interface IPlugin
{
    string PluginDescription { get; }
    void DoWork();
}

Deze klasse zou zich in een afzonderlijke dll bevinden

class HelloPlugin : IPlugin
{
    public string PluginDescription => "A plugin that says Hello";
    public void DoWork()
    {
        Console.WriteLine("Hello");
    }
}

De plug-inlader van uw toepassing zou de dll-bestanden vinden, alle typen in die samenstellingen krijgen die IPlugin implementeren, en instanties van die maken.

    public IEnumerable<IPlugin> InstantiatePlugins(string directory)
    {
        var pluginAssemblyNames = Directory.GetFiles(directory, "*.addin.dll").Select(name => new FileInfo(name).FullName).ToArray();
        //load the assemblies into the current AppDomain, so we can instantiate the types later
        foreach (var fileName in pluginAssemblyNames)
            AppDomain.CurrentDomain.Load(File.ReadAllBytes(fileName));
        var assemblies = pluginAssemblyNames.Select(System.Reflection.Assembly.LoadFile);
        var typesInAssembly = assemblies.SelectMany(asm => asm.GetTypes());
        var pluginTypes = typesInAssembly.Where(type => typeof (IPlugin).IsAssignableFrom(type));
        return pluginTypes.Select(Activator.CreateInstance).Cast<IPlugin>(); 
    }

Een instantie van een type maken

De eenvoudigste manier is om de Activator klasse te gebruiken.

Hoewel de prestaties van Activator zijn verbeterd sinds .NET 3.5, is het gebruik van Activator.CreateInstance() soms een slechte optie vanwege (relatief) lage prestaties: test 1 , test 2 , test 3 ...


Met Activator klasse

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

U kunt een objectarray doorgeven aan Activator.CreateInstance als u meer dan één parameter hebt.

// With a constructor such as MyClass(int, int, string)
Activator.CreateInstance(typeof(MyClass), new object[] { 1, 2, "Hello World" });

Type type = typeof(someObject);
var instance = Activator.CreateInstance(type);

Voor een generiek type

De methode MakeGenericType verandert een open generiek type (zoals List<> ) in een concreet type (zoals List<string> ) door er MakeGenericType op toe te passen.

// 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);

De List<> syntax is niet toegestaan buiten een typeof expressie.


Zonder Activator klasse

new trefwoord gebruiken (voldoet voor constructors zonder parameter)

T GetInstance<T>() where T : new()
{
    T instance = new T();
    return instance;
}

Gebruik de Invoke-methode

// 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" });

Expressiebomen gebruiken

Expressiebomen vertegenwoordigen code in een boomachtige gegevensstructuur, waarbij elk knooppunt een expressie is. Zoals MSDN uitlegt:

Expressie is een reeks van een of meer operanden en nul of meer operatoren die kunnen worden geëvalueerd tot een enkele waarde, object, methode of naamruimte. Uitdrukkingen kunnen bestaan uit een letterlijke waarde, een methode-aanroep, een operator en de operanden, of een eenvoudige naam. Simpele namen kunnen de naam zijn van een variabele, type lid, methode parameter, naamruimte of 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 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]);
                    }
                    var newExpr = Expression.New(ci, argExpressions); // create expression that represents call to specified ctor with the specified arguments.
  
                    _registeredTypes.Add(key, Expression.Lambda(newExpr, new[] { pExp }).Compile() as Func<object[], TType>); // compile expression to create delegate, and add fucntion to dictionary
                }
            }
        }

        /// <summary>
        /// Returns instance of registered type by key.
        /// </summary>
        /// <typeparam name="TType"></typeparam>
        /// <param name="key"></param>
        /// <param name="args"></param>
        /// <returns></returns>
        public TType Create(TKey key, params object[] args)
        {
            Func<object[], TType> foo;
            if (_registeredTypes.TryGetValue(key, out foo))
            {
                return (TType)foo(args);
            }

            throw new ArgumentException("No type registered for this key.");
        }
    }

Kan als volgt worden gebruikt:

 public class TestClass
 {
        public TestClass(string parameter)
        {
            Console.Write(parameter);
        }
 } 


public void TestMethod()
{
       var factory = new GenericFactory<string, TestClass>();
       factory.Register("key", typeof(string));
       TestClass newInstance = factory.Create("key", "testParameter");
}

FormatterServices.GetUninitializedObject gebruiken

T instance = (T)FormatterServices.GetUninitializedObject(typeof(T));

In het geval van het gebruik van FormatterServices.GetUninitializedObject constructors en veldinitializers worden niet aangeroepen. Het is bedoeld om te worden gebruikt in serializers en remoting-motoren

Krijg een Type op naam met naamruimte

Om dit te doen heeft u een verwijzing nodig naar het merk dat het type bevat. Als je een ander type beschikbaar hebt waarvan je weet dat het in dezelfde assembly zit als degene die je wilt, kun je dit doen:

typeof(KnownType).Assembly.GetType(typeName);
  • waarbij typeName de naam is van het type dat u zoekt (inclusief de naamruimte) en KnownType het type is waarvan u weet dat het zich in dezelfde assembly bevindt.

Minder efficiënt maar algemener is als volgt:

Type t = null;
foreach (Assembly ass in AppDomain.CurrentDomain.GetAssemblies())
{
    if (ass.FullName.StartsWith("System."))
        continue;
    t = ass.GetType(typeName);
    if (t != null)
        break;
}

Let op het vinkje om het scannen van systeemnaamruimte-assemblages uit te sluiten om het zoeken te versnellen. Als uw type mogelijk een CLR-type is, moet u deze twee regels verwijderen.

Als u de volledig typegekwalificeerde typenaam hebt, inclusief de assemblage, kunt u deze eenvoudig verkrijgen

Type.GetType(fullyQualifiedName);

Krijg een sterk getypeerde gemachtigde voor een methode of eigenschap via reflectie

Wanneer prestaties een probleem zijn, is het niet ideaal om een methode via reflectie (dat wil zeggen via de MethodInfo.Invoke methode) in te roepen. Het is echter relatief eenvoudig om met de functie Delegate.CreateDelegate een beter presterende, sterk getypeerde afgevaardigde te verkrijgen. De prestatievergoeding voor het gebruik van reflectie wordt alleen opgelopen tijdens het proces van het maken van deelnemers. Zodra de gemachtigde is aangemaakt, is er weinig tot geen prestatievergoeding voor het beroep op:

// Get a MethodInfo for the Math.Max(int, int) method...
var maxMethod = typeof(Math).GetMethod("Max", new Type[] { typeof(int), typeof(int) });
// Now get a strongly-typed delegate for Math.Max(int, int)...
var stronglyTypedDelegate = (Func<int, int, int>)Delegate.CreateDelegate(typeof(Func<int, int, int>), null, maxMethod);
// Invoke the Math.Max(int, int) method using the strongly-typed delegate...
Console.WriteLine("Max of 3 and 5 is: {0}", stronglyTypedDelegate(3, 5));

Deze techniek kan ook worden uitgebreid tot eigenschappen. Als we een klasse met de naam MyClass met een int eigenschap met de naam MyIntProperty , zou de code om een sterk getypte getter te krijgen zijn (het volgende voorbeeld veronderstelt dat 'target' een geldig exemplaar van MyClass ):

// Get a MethodInfo for the MyClass.MyIntProperty getter...
var theProperty = typeof(MyClass).GetProperty("MyIntProperty");
var theGetter = theProperty.GetGetMethod();
// Now get a strongly-typed delegate for MyIntProperty that can be executed against any MyClass instance...
var stronglyTypedGetter = (Func<MyClass, int>)Delegate.CreateDelegate(typeof(Func<MyClass, int>), theGetter);
// Invoke the MyIntProperty getter against MyClass instance 'target'...
Console.WriteLine("target.MyIntProperty is: {0}", stronglyTypedGetter(target));

... en hetzelfde kan worden gedaan voor de setter:

// Get a MethodInfo for the MyClass.MyIntProperty setter...
var theProperty = typeof(MyClass).GetProperty("MyIntProperty");
var theSetter = theProperty.GetSetMethod();
// Now get a strongly-typed delegate for MyIntProperty that can be executed against any MyClass instance...
var stronglyTypedSetter = (Action<MyClass, int>)Delegate.CreateDelegate(typeof(Action<MyClass, int>), theSetter);
// Set MyIntProperty to 5...
stronglyTypedSetter(target, 5);


Modified text is an extract of the original Stack Overflow Documentation
Licentie onder CC BY-SA 3.0
Niet aangesloten bij Stack Overflow