I'm trying to get a struct within a struct using reflection (I'm using structs to Marshal some inherited C DLL structures):
public struct Struct1
{
public Int32 Value;
}
public struct Struct2
{
// I0/I1 are substructures in an "Unrolled" array.
public Struct1 I0;
public Struct1 I1;
}
Then, in my program:
class Program
{
static void Main(string[] args)
{
Struct2 MyStr = new Struct2();
MyStr.I0.Value = 0;
MyStr.I1.Value = 1;
// I want to access I0/I1 using reflection.
for (int i =0; i<2;i++) {
string structname = "I"+i;
FieldInfo FI = typeof(Struct2).GetType().GetField(structname, BindingFlags.Public | BindingFlags.Instance);
object StructureWeWant = FI.GetValue(MyStr); // Tool errors here, saying FI is empty.
FieldInfo ValueFieldInsideStruct1 = typeof(Struct1).GetField("Value");
Int32 ValueIWantToPrint = (Int32) ValueFieldInsideStruct1.GetValue(StructureWeWant);
Console.WriteLine(structname + " " + ValueIWantToPrint);
}
}
}
Anyone know where my error is? I would think structs would be accessible by GetField() but maybe they're not?
typeof(Struct2).GetType() gives you a System.RuntimeType, not your struct. Remove GetType() to get it working.
Replace this:
FieldInfo FI = typeof(Struct2).GetType().GetField(structname, BindingFlags.Public | BindingFlags.Instance);
with this:
FieldInfo FI = typeof(Struct2).GetField(structname, BindingFlags.Public | BindingFlags.Instance);
Related
Consider the following C# class declaration:
public class MyClass {
private enum Colours { Red, Green, Blue }
}
Which is sat in a separate class library/DLL.
Given just the typeof(MyClass) object (System.Type), is there any way to check if the class contains an enum called Colours at runtime and if so return it's corresponding System.Type object?
What I'm trying to do is write some generic code that's given the type of a class and determine if contains a specifically named enum inside and then query the values in the enum.
I know how to use Reflection to query things like GetFields, GetProperties etc. but there isn't a GetClasses or GetEnums method in System.Type.
I suspect this kind of information is in the assembly?
Just do:
var res = typeof(MyClass).GetNestedType("Colours", BindingFlags.NonPublic);
Test res != null to see if such type exists.
Then test res.IsEnum to see if the nested type is an enum.
Addition: If the nested type is occasionally nested public, use BindingFlags.NonPublic | BindingFlags.Public instead.
I came up with following two methods:
public class MyClass {
private enum Colours { Red, Green, Blue }
private class Inner {
private enum Colours { Black, White }
}
}
class Program {
static void Main(string[] args) {
Type coloursType;
// 1. enumerator
coloursType = typeof(MyClass).EnumerateNestedTypes()
.Where(t => t.Name == "Colours" && t.IsEnum)
.FirstOrDefault();
// 2. search method
coloursType = typeof(MyClass).FindNestedType(t => t.Name == "Colours" && t.IsEnum);
if(coloursType != null) {
Console.WriteLine(string.Join(", ", coloursType.GetEnumNames()));
} else {
Console.WriteLine("Type not found");
}
Console.ReadKey();
}
}
public static class Extensions {
public static IEnumerable<Type> EnumerateNestedTypes(this Type type) {
const BindingFlags flags = BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic;
Queue<Type> toBeVisited = new Queue<Type>();
toBeVisited.Enqueue(type);
do {
Type[] nestedTypes = toBeVisited.Dequeue().GetNestedTypes(flags);
for(int i = 0, l = nestedTypes.Length; i < l; i++) {
Type t = nestedTypes[i];
yield return t;
toBeVisited.Enqueue(t);
}
} while(toBeVisited.Count != 0);
}
public static Type FindNestedType(this Type type, Predicate<Type> filter) {
const BindingFlags flags = BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic;
Type[] nestedTypes = type.GetNestedTypes(flags);
foreach(var nestedType in nestedTypes) {
if(filter(nestedType)) {
return nestedType;
}
}
foreach(var nestedType in nestedTypes) {
Type result = FindNestedType(nestedType, filter);
if(result != null) {
return result;
}
}
return null;
}
}
var types = typeof(MyClass).Assembly.DefinedTypes;
foreach (var type in types)
{
Console.WriteLine(type.Name);
}
Output:
MyClass
Colours
Recently I was writing a method to construct a graph with the dependencies between classes using Reflection and found the following problem. My method analyzes the return type of property, generic arguments of class definition and instance fields of those classes.
For inspection of instance fields of class I use the following method.
public static IEnumerable<FieldInfo> GetFields(Type classType)
{
return classType
.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
}
To test it, I write the following class definition:
static void Main(string[] args)
{
foreach (var fieldInfo in GetFields(typeof(A)))
Console.WriteLine(fieldInfo.Name);
Console.ReadKey();
}
class A
{
private ulong? _field1;
public byte PropertyA { get; set; }
public int PropertyB { get; set; }
public bool PropertyC { get; set; }
}
I was in shock for a few seconds, to see the result. It was when I remembered that .NET generates an instance field, Set and Get methods to emulate the properties.
When I inspected with .NET Reflector the library to see the code generated by the compiler, I find the following definition.
class A
{
private ulong? _field1;
[CompilerGenerated]
private Byte <PropertyA>k__BackingField;
[CompilerGenerated]
private Int32 <PropertyB>k__BackingField;
[CompilerGenerated]
private bool <PropertyC>k__BackingField;
}
So I modified the method to exclude the fields with CompilerGenerated attribute and his name match with some property.
public static IEnumerable<FieldInfo> GetFields(Type classType)
{
var regex = new Regex(#"^<(?<PropertyName>\w+)>\w+$");
var fieldInfoes = classType
.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
foreach (var fieldInfo in fieldInfoes)
{
if (fieldInfo.GetCustomAttribute<CompilerGeneratedAttribute>() == null)
yield return fieldInfo;
else
{
var match = regex.Match(fieldInfo.Name);
if (!match.Success)
continue;
var propertyName = match.Groups[#"PropertyName"].Value;
if (classType.GetProperty(propertyName) == null)
yield return fieldInfo;
}
}
}
QUESTIONS
There is some combination of BindingFlags I'm missing for these fields?
There is another way of getting these fields because this code appears to me that is like killing a mosquito with a bazooka?
You can download the complete code here.
You need the fields that are: !field.IsDefined(typeof(CompilerGeneratedAttribute), false)
public static IEnumerable<FieldInfo> GetFields(Type classType)
{
var allFields = classType
.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
var definedFields = from field in allFields
where !field.IsDefined(typeof(CompilerGeneratedAttribute), false)
select field;
return definedFields;
}
I'm trying to clone instances of a derived class, but somehow it doesn't work well. The cloning method is:
public static T CloneFieldsAndProperties<T>(T input)
{
T result = (T)Activator.CreateInstance(typeof(T));
PropertyInfo[] listOfProps = typeof(T).GetProperties(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.FlattenHierarchy | BindingFlags.CreateInstance);
FieldInfo[] listOfFields = typeof(T).GetFields(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.FlattenHierarchy | BindingFlags.CreateInstance);
foreach (PropertyInfo prop in listOfProps) prop.SetValue(result, prop.GetValue(input, null), null);
foreach (FieldInfo field in listOfFields) field.SetValue(result, field.GetValue(input));
return result;
}
As you can see, I added many BindingFlags because it wasn't working. But to no avail.
It does work in a simple case:
MyclassA1 a1 = new MyclassA1();
MyclassA a = CloneFieldsAndProperties(a1);
if (a is MyclassA1) Text = "Works";
Where:
class MyclassA
{
public int i;
}
class MyclassA1 : MyclassA
{
public int i1;
}
But in my real program it doesn't. The real program's classes' declarations are long so I'm not posting them here. What might be the problem?
I had the same issue a long time ago. The only real solution for me, after lots of googling, was to serialize and deserialize it. It's not a bad solution and you lose only a little bit of performance, just do it like this:
Add this tag to your class:
[Serializable()]
public class a
{
}
And then you can create a function like this:
public object Clone()
{
IO.MemoryStream mem = new IO.MemoryStream();
System.Runtime.Serialization.Formatters.Binary.BinaryFormatter form = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
form.Serialize(mem, this);
mem.Position = 0;
return form.Deserialize(mem);
}
If you need a shallow clone, simply use Object.MemberwiseClone. If you need a deep clone, serialize and then deserialize your object (e.g. using BinaryFormatter or DataContractSerializer). This would take care of issues such as cycles and cross references.
This will work and may be faster than the serialization method:
Code:
using System;
namespace Cloning
{
class Program
{
static void Main(string[] args)
{
Derived d = new Derived() { property = 1, field = 2, derivedProperty = 3, derivedField = 4 };
Base b = new Derived(d);
// Change things in the derived class.
d.property = 5;
d.field = 6;
d.derivedProperty = 7;
d.derivedField = 8;
// Output the copy so you know it's not shallow.
Console.WriteLine((b as Derived).property);
Console.WriteLine((b as Derived).field);
Console.WriteLine((b as Derived).derivedProperty);
Console.WriteLine((b as Derived).derivedField);
Console.ReadLine();
}
class Base
{
public int property { get; set; }
public int field;
}
class Derived : Base
{
public Derived() { }
public Derived(Derived d)
{
// Perform the deep copy here.
// Using reflection should work, but explicitly stating them definitely
// will, and it's not like it's not all known until runtime.
this.property = d.property;
this.field = d.field;
this.derivedProperty = d.derivedProperty;
this.derivedField = d.derivedField;
}
public int derivedProperty { get; set; }
public int derivedField;
}
}
}
Test:
http://goo.gl/pQnAL
Output:
1
2
3
4
Comments:
I would really imagine that this would work in more than just a trivial case but perhaps not:
public static T CloneFieldsAndProperties<T>(T input)
{
T result = (T)Activator.CreateInstance(input.GetType());
PropertyInfo[] properties = input.GetType().GetProperties();
FieldInfo[] fields = input.GetType().GetFields();
foreach (PropertyInfo property in properties)
property.SetValue(result, property.GetValue(input, null), null);
foreach (FieldInfo field in fields)
field.SetValue(result, field.GetValue(input));
return result;
}
I need to assign an array to a field. I dont know the fields type, but I do have a reference to an instance and the name of the field. I can assume the array can be casted to the fields type. Can this be done?
Bas
Edit:
Hopefully this code will clarify what Im trying to do, this causes an exception in assign:
class MyClass
{
static void Main()
{
MyClass t = new MyClass();
A a = new A();
C[] c = new C[] {new B()};
t.Assign(a, "field", c);
}
void Assign(object obj, string field, object[] value)
{
// crashes
obj.GetType().GetField(field).SetValue(obj, value);
}
}
class A
{
public B[] field;
}
class B : C { }
class C { }
instance.GetType()
.GetField("fieldName", BindingFlags.Instance | BindingFlags.NonPublic)
.SetValue(instance, array);
For more information see the reflection page on MSDN.
If the code which calls this is not timecritical you can simply use the FieldInfos SetValue()
obj.GetField(fieldName, BindingFlags.Instance | BindingFlags.NonPublic).SetValue(obj, newFieldValue);
If the code will be called more often you might want to dynamically compile a setter-delegate. This can be done e.g. using lightweight code generation:
Action<TObject, TField> ConstructGetter(string fieldName)
{
System.Reflection.FieldInfo field = typeof(TObject).GetField(fieldName);
DynamicMethod method = new DynamicMethod(typeof(TObject).ToString() + ":" + "Set:" + name,
null, new Type[] { typeof(TObject), typeof(TField) }, typeof(TObject));
ILGenerator generator = method.GetILGenerator();
generator.Emit(OpCodes.Ldarg_0);
generator.Emit(OpCodes.Ldarg_1);
generator.Emit(OpCodes.Stfld, field);
generator.Emit(OpCodes.Ret);
return method.CreateDelegate(typeof(Action<TObject, TField>)) as Action<TObject, TField>;
}
So a kind soul showed me the solution, enjoy : )
using System;
using System.Reflection;
class MyClass
{
static void Main()
{
MyClass t = new MyClass();
A a = new A();
C[] c = new C[] {new B()};
t.Assign(a, "field", c);
}
void Assign(object obj, string field, object[] value)
{
FieldInfo pinfo = obj.GetType().GetField(field);
Array array = Array.CreateInstance(pinfo.FieldType.GetElementType(), value.Length);
value.CopyTo(array, 0);
pinfo.SetValue(obj, array);
}
}
class A
{
public B[] field;
}
class B : C { }
class C { }
I'm trying to get the field info of an array value from within a struct. So far I have the following, but I dont see how to get the infomration I want.
[StructLayout(LayoutKind.Sequential)]
public struct Test
{
public byte Byte1;
[MarshalAs(UnmanagedType.ByValArray, SizeConst=3)]
public Test2[] Test1;
}
BindingFlags struct_field_flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance | BindingFlags.DeclaredOnly;
FieldInfo[] all_struct_fields = typeof(Test).GetFields(struct_field_flags);
foreach (FieldInfo struct_field in all_struct_fields)
{
if(struct_field.FieldType.IsArray)
{
// Get FieldInfo for each value in the Test1 array within Test structure
}
}
So if I did:
Type array_type = struct_field.FieldType.GetElementType();
This would return Test2 type, but I dont want the type of the array, I want the FieldInfo or Fields of that structure so I can set values from within it.
Sorry for the initial wrong answer. I was too lazy to create my own Test2 type so I used a string instead. Here's the right answer (hopefully):
I did what you want to do with the following code:
class Program
{
static void Main(string[] args)
{
object sampleObject = GetSampleObject();
FieldInfo[] testStructFields = typeof(Test).GetFields();
foreach (FieldInfo testStructField in testStructFields)
{
if (testStructField.FieldType.IsArray)
{
// We can cast to ILIst because arrays implement it and we verfied that it is an array in the if statement
System.Collections.IList sampleObject_test1 = (System.Collections.IList)testStructField.GetValue(sampleObject);
// We can now get the first element of the array of Test2s:
object sampleObject_test1_Element0 = sampleObject_test1[0];
// I hope this the FieldInfo that you want to get:
FieldInfo myValueFieldInfo = sampleObject_test1_Element0.GetType().GetField("MyValue");
// Now it is possible to read and write values
object sampleObject_test1_Element0_MyValue = myValueFieldInfo.GetValue(sampleObject_test1_Element0);
Console.WriteLine(sampleObject_test1_Element0_MyValue); // prints 99
myValueFieldInfo.SetValue(sampleObject_test1_Element0, 55);
sampleObject_test1_Element0_MyValue = myValueFieldInfo.GetValue(sampleObject_test1_Element0);
Console.WriteLine(sampleObject_test1_Element0_MyValue); // prints 55
}
}
}
static object GetSampleObject()
{
Test sampleTest = new Test();
sampleTest.Test1 = new Test2[5];
sampleTest.Test1[0] = new Test2() { MyValue = 99 };
object sampleObject = sampleTest;
return sampleObject;
}
}
[StructLayout(LayoutKind.Sequential)]
public struct Test2
{
public int MyValue;
}
[StructLayout(LayoutKind.Sequential)]
public struct Test
{
public byte Byte1;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
public Test2[] Test1;
}
This is the most important line:
FieldInfo myValueFieldInfo = sampleObject_test1_Element0.GetType().GetField("MyValue");
It should give you the FieldInfo that you are talking about.
What exactly are you after? There is no FieldInfo for the items in the array... you can iterate the values by getting the array (as Array) and iterating it... just use:
Array arr = (Array)field.GetValue(obj);
The problem with #weiqure's technique is that it only works if the array already has at least one element in it. Here is a way to find the array's element type, whether it contains elements or not:
bool GetArrayElementType(FieldInfo field, out Type elementType)
{
if (field.FieldType.IsArray && field.FieldType.FullName.EndsWith("[]"))
{
string fullName = field.FieldType.FullName.Substring(0, field.FieldType.FullName.Length - 2);
elementType = Type.GetType(string.Format("{0},{1}", fullName, field.FieldType.Assembly.GetName().Name));
return true;
}
elementType = null;
return false;
}
And here is how you would use that function:
void Test(object targetObject, string fieldName)
{
FieldInfo field = targetObject.GetType().GetField(fieldName);
Type elementType;
bool success = GetArrayElementType(field, out elementType);
}