How do I get the FieldInfo of an array field? - 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);
}

Related

Get type of enum in .NET class

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

Get with Reflection fields that are not generated by the compiler

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;
}

Failing to get fields of an object

Simple c# console application to check how to get fields of an unknown object.
public class A
{
public int id;
public string name;
public string desc;
}
class Program
{
static void Main(string[] args)
{
A a = new A();
getProp(a);
Console.ReadKey();
}
static object getProp(object o)
{
Type type = o.GetType();
PropertyInfo[] pros = type.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
//Do Something
return null;
}
}
I am not getting any fields. pros has no value inside it. I have to get the field names of the object o.
The members which you're trying to fetch are not properties, but fields. Try the following:
var fields = typeof(A).GetFields();
Or:
static FieldInfo[] GetFields(object o)
{
Type type = o.GetType();
FieldInfo[] fields = type.GetFields();
return fields;
}
And in order to grab the object's fields values:
var fields = GetFields(obj);
foreach(var field in fields)
{
Console.WriteLine(field.GetValue(obj));
}
From MSDN:
Type.GetFields Method
Returns all the public fields of the current Type.

Get struct within struct using reflection

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

Why does this cloning not work?

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;
}

Categories

Resources