I can't understand why my fieldType is not qualified as a generic list. I tried to use the solution proposed below to get the generic type of a list. But when I reach fieldType.IsGenericType == false, I was quite surprised and I don't understand what is happening.
My goal is to be able to use my method CreateTable() for each field in my context. My context should only have List fields.
This is the information i found in my object:
FieldType = {Name = "List`1" FullName =
"System.Collections.Generic.List`1[[WebApplication1.Models.Movie, WebApplication1,
Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]"}
Reference : C# generic list <T> how to get the type of T?
My code example:
public class MediaContext
{
public List<Movie> Movies { get; set; }
public List<Subtitle> Subtitles { get; set; }
public MediaContext()
{
this.Movies = new List<Movie>();
this.Subtitles = new List<Subtitle>();
}
}
public void CreateDB(object context)
{
Type type = context.GetType();
FieldInfo[] fields = type.GetFields(BindingFlags.Instance
| BindingFlags.NonPublic);
foreach (FieldInfo field in fields)
{
Type genericType = this.GetGenericType(field);
MethodInfo method = this.GetType().GetMethod("CreateTable");
MethodInfo generic = method.MakeGenericMethod(genericType);
generic.Invoke(this, null);
}
foreach (FieldInfo field in fields)
{
Type genericType = this.GetGenericType(field);
MethodInfo method = this.GetType().GetMethod("AddKeys");
MethodInfo generic = method.MakeGenericMethod(genericType);
generic.Invoke(this, null);
}
}
private Type GetGenericType(FieldInfo field)
{
Type fieldType = field.GetType();
Type genericType = null;
// Where I believe is should be generic.
if (fieldType.IsGenericType &&
fieldType.GetGenericTypeDefinition() == typeof(List<>))
{
genericType = fieldType.GetGenericArguments()[0];
}
else
{
throw new Exception("An array is needed");
}
return genericType;
}
public void CreateTable<T>()
{
StringBuilder query = new StringBuilder();
Type type = typeof(T);
query.Append(String.Format("CREATE TABLE {0} (", NamingGeneration.PlurializeName(type.Name)));
query.Append(this.AddFields(typeof(T).GetProperties()));
query.Append(")");
SqlCommand command = new SqlCommand();
command.CommandText = query.ToString();
SqlExecuteRequest.Instance.ExecuteNonQuery(command);
}
private void AddKeys<T>()
{
Type type = typeof(T);
PropertyInfo[] properties = type.GetProperties();
IEnumerable<PropertyInfo> keyedProperties = properties.Where(x => x.Name.Contains("Id"));
foreach (PropertyInfo property in keyedProperties)
{
if (property.Name == "Id")
{
this.AddPrimaryKey(type.Name, property.Name);
}
else if (property.Name.EndsWith("Id"))
{
this.AddForeignKey(type.Name, property.Name);
}
}
}
To fix the code as you have it, instead of
Type fieldType = field.GetType();
You want
Type fieldType = field.FieldType;
And it will work. FieldInfo is what you pass through. When you call GetType(), the type you're getting is the FieldInfo type information. FieldInfo contains information about a Field, and the Type information is held in FieldInfo.FieldType.
If you step through this code, you would see this behavior. Debugging is useful.
Related
I'm trying to make this code in IL using ILGenerator.Emit
class Foo
{
...
}
public class TestClass
{
public static void test()
{
Type t = typeof(Foo);
foreach(PropertyInfo p in t.GetProperties())
{
GenerateVar(p.PropertyInfo);
}
}
public static object GenerateVar(Type var)
{
if (var == typeof(Int32))
{
return 5;
}
else if (var == typeof(Char))
{
return 'a';
}
return null;
}
}
This is what I've done so far, and had some complications:
MethodInfo mi = TestClass.GetType().GetMethod("GenerateVar",
BindingFlags.Public |
BindingFlags.Instance);
ILGenerator generator = mb.GetILGenerator();
LocalBuilder propertyType;
LocalBuilder TestClass = mb_gen.DeclareLocal(typeof(TestClass));
foreach (PropertyInfo pi in t.GetProperties())
{
propertyType = mb_gen.DeclareLocal(pi.PropertyType);
//loads into the stack the current PropertyType and the method class
generator.Emit(OpCodes.Ldloc, TestClass);
generator.Emit(OpCodes.LdLoc, propertyType);
//calls GenerateVar(Type var) to get a PropertyType var
generator.Emit(OpCodes.Callvirt, mi);
}
It gives me the following exception:
-> expected type: System.Type , Received type: System.String
System.String is the property type that was given by: pi.PropertyType;
What am I doing wrong?
Thanks in advance
As thehennyy comment, if you can give us the full code we will can to help better. I'm trying to help here because I guess what you try to do.
So I based here on your C# code. As I understand you want to create a method that get properties of type (Foo in your case) and foreach of them, get some value based on type.
Here is a snippet of doing it for first property of the type. To complete the code you need to emit the loop, or, like you wrote in your question, loop on the properties in your C# code and emit the code for each property one after one.
static void CallGenerateVar()
{
var dm = new DynamicMethod("CallGenerateVar", typeof(object), Type.EmptyTypes, typeof(TestClass));
MethodInfo generateVarMethod = typeof(TestClass).GetMethod("GenerateVar", BindingFlags.Public | BindingFlags.Instance);
var ilGen = dm.GetILGenerator();
var properties = ilGen.DeclareLocal(typeof(PropertyInfo[]));
var index = ilGen.DeclareLocal(typeof(int));
var propInfo = ilGen.DeclareLocal(typeof(PropertyInfo));
ilGen.Emit(System.Reflection.Emit.OpCodes.Ldtoken, typeof(Foo));
ilGen.Emit(System.Reflection.Emit.OpCodes.Call, typeof(Type).GetMethod("GetTypeFromHandle", BindingFlags.Static | BindingFlags.Public));
ilGen.Emit(System.Reflection.Emit.OpCodes.Callvirt, typeof(Type).GetMethod("GetProperties", Type.EmptyTypes));
ilGen.Emit(System.Reflection.Emit.OpCodes.Stloc_0);
ilGen.Emit(System.Reflection.Emit.OpCodes.Ldc_I4_0);
ilGen.Emit(System.Reflection.Emit.OpCodes.Stloc_1);
ilGen.Emit(System.Reflection.Emit.OpCodes.Ldloc_0);
ilGen.Emit(System.Reflection.Emit.OpCodes.Ldloc_1);
ilGen.Emit(System.Reflection.Emit.OpCodes.Ldelem_Ref);
ilGen.Emit(System.Reflection.Emit.OpCodes.Stloc_2);
ilGen.Emit(System.Reflection.Emit.OpCodes.Ldloc_0);
ilGen.Emit(System.Reflection.Emit.OpCodes.Ldloc_2);
ilGen.Emit(System.Reflection.Emit.OpCodes.Callvirt, typeof(PropertyInfo).GetMethod("get_PropertyType", BindingFlags.Instance | BindingFlags.Public));
ilGen.Emit(System.Reflection.Emit.OpCodes.Callvirt, generateVarMethod);
ilGen.Emit(System.Reflection.Emit.OpCodes.Ret);
var del = (Func<object>)dm.CreateDelegate(typeof(Func<object>));
var result = del.Invoke();
}
In case our Foo type looks like this:
class Foo
{
public int MyProperty { get; set; }
}
And GenerateVar looks like this:
public object GenerateVar(Type var)
{
if (var == typeof(Int32))
{
return 5;
}
else if (var == typeof(Char))
{
return 'a';
}
return null;
}
It will print 5
for example, if I have a User class with UserType class inside like this:
public class User
{
public int UserID {get; set;}
public string UserName {get; set;}
public string DisplayName {get; set;}
public UserType UserType {get; set;}
}
public class UserType
{
public int UserTypeID {get; set;}
public string Name {get; set;}
public string Description {get; set;}
}
Then I want a method that will convert List of user to DataTable.
List<User> Users = new List<User>();
DataTable dt = ConvertToDataTable(Users)
Currently, I have a way to convert List of an objectClass to DataTable, but when there is an object embedded inside it like this (UserType object), the conversion will be failed. Is it possible that I can convert List<User> to DataTable and the field in UserType will have column name as "UserType.Name", "UserType.Description"
For Example, by conversing a List<User> object to DataTable, it will have following columns:
UserID UserName DisplayName UserType.UserTypeID UserType.Name UserType.Description
Per your request, this is the method that I use to convert a List to DataTable
///<summary> Convert List of Object to Data Table (To Display in Data Table). </summary>
public static DataTable From_Obj_Lst(object list)
{
DataTable dt = null;
Type listType = list.GetType();
if (listType.IsGenericType)
{
//Determine the underlying type the List<> contains
Type elementType = listType.GetGenericArguments()[0];
//create empty table -- give it a name in case
//it needs to be serialized
dt = new DataTable(elementType.Name + "List");
//define the table -- add a column for each public
//property or field
MemberInfo[] miArray = elementType.GetMembers(
BindingFlags.Public | BindingFlags.Instance);
foreach (MemberInfo mi in miArray)
{
if (mi.MemberType == MemberTypes.Property)
{
PropertyInfo pi = mi as PropertyInfo;
dt.Columns.Add(pi.Name, Nullable.GetUnderlyingType(pi.PropertyType) ?? pi.PropertyType);
}
else if (mi.MemberType == MemberTypes.Field)
{
FieldInfo fi = mi as FieldInfo;
dt.Columns.Add(fi.Name, fi.FieldType);
}
}
//populate the table
IList il = list as IList;
foreach (object record in il)
{
int i = 0;
object[] fieldValues = new object[dt.Columns.Count];
foreach (DataColumn c in dt.Columns)
{
MemberInfo mi = elementType.GetMember(c.ColumnName)[0];
if (mi.MemberType == MemberTypes.Property)
{
PropertyInfo pi = mi as PropertyInfo;
fieldValues[i] = pi.GetValue(record, null);
}
else if (mi.MemberType == MemberTypes.Field)
{
FieldInfo fi = mi as FieldInfo;
fieldValues[i] = fi.GetValue(record);
}
i++;
}
dt.Rows.Add(fieldValues);
}
}
//Finally
return dt;
}
I like your idea to convert List of some class into DataTable and I have some situation where I can get benefit of using this function. So I spare sometime to make your existing function work with an object containing property of type another object/class.
public static DataTable From_Obj_Lst(object list)
{
DataTable dt = null;
Type listType = list.GetType();
if (listType.IsGenericType)
{
Type elementType = listType.GetGenericArguments()[0];
dt = new DataTable(elementType.Name + "List");
AddColumns(ref dt, elementType, "");
IList il = list as IList;
foreach (object record in il)
{
int i = 0;
object[] fieldValues = new object[dt.Columns.Count];
foreach (DataColumn c in dt.Columns)
{
fieldValues[i] = GetValueByColumnName(c.ColumnName, record);
i++;
}
dt.Rows.Add(fieldValues);
}
}
return dt;
}
I moved adding columns operations to another method and applied some sort of recursive behavior so that if a property is of type ClassA the method will call it self to add columns for each property in ClassA. Since the method is recursive, I expect this to work fine when ClassA has a property of type ClassB, and ClassB has a property of type ClassC and so on -haven't tested with more than two levels-. And here I am using property name as column prefix instead of property type name to avoid duplicate column name, so the function will work when an object has two properties with same Type.
public static void AddColumns(ref DataTable dt, Type elementType, string columnPrefix)
{
MemberInfo[] miArray = elementType.GetMembers(
BindingFlags.Public | BindingFlags.Instance);
foreach (MemberInfo mi in miArray)
{
if (mi.MemberType == MemberTypes.Property)
{
PropertyInfo pi = mi as PropertyInfo;
if (pi.PropertyType.IsPrimitive || pi.PropertyType == typeof(String))
dt.Columns.Add(columnPrefix + pi.Name, Nullable.GetUnderlyingType(pi.PropertyType) ?? pi.PropertyType);
else AddColumns(ref dt, pi.PropertyType, String.Format("{0}{1}.", columnPrefix, pi.Name));
}
else if (mi.MemberType == MemberTypes.Field)
{
FieldInfo fi = mi as FieldInfo;
if (fi.FieldType.IsPrimitive || fi.FieldType == typeof(String))
dt.Columns.Add(columnPrefix + fi.Name, fi.FieldType);
else AddColumns(ref dt, fi.FieldType, String.Format("{0}{1}.", columnPrefix, fi.Name));
}
}
}
Logic to get value for every column in a row is a reverse of logic to add column name from property. Properties having value = null is also handled properly -tested this one-.
public static object GetValueByColumnName(string colName, object record)
{
var isComplexProperty = colName.Split('.').Length > 1;
if (!isComplexProperty)
{
return GetSimplePropertyValue(colName, record);
}
else
{
var propertyName = colName.Split('.')[0];
var propertyValue = GetSimplePropertyValue(propertyName, record);
if (propertyValue != null)
return GetValueByColumnName(colName.Replace(propertyName + ".", ""), propertyValue);
}
return null;
}
public static object GetSimplePropertyValue(string propName, object record)
{
Type elementType = record.GetType();
MemberInfo mi = elementType.GetMember(propName)[0];
if (mi.MemberType == MemberTypes.Property)
{
PropertyInfo pi = mi as PropertyInfo;
return pi.GetValue(record, null);
}
else if (mi.MemberType == MemberTypes.Field)
{
FieldInfo fi = mi as FieldInfo;
return fi.GetValue(record);
}
return null;
}
What you need is "PropertyInfo" in your converter, when you find a property is int or string, you can directly get the value, or else, recursively get the embeded object values.
PropertyInfo[] propList = obj.GetType().GetProperties(); //This will get all property with property name
foreach (PropertyInfo pInfo in propList)
{
if (pInfo.PropertyType == typeof(int) || pInfo.PropertyType == typeof(string))
{
//Just get the value and insert to your table
object propValue = pInfo.GetValue(obj, null); //Notice this is not fit for array type
}
else
{
//This is embeded object
string thisPropName = pInfo.Name; //Get the property name. Here should be UserType
object propValue = pInfo.GetValue(obj, null); //Then you can use this object to get its inside property name and value with same method above.
}
}
i have used the following code to change the current value for the current field value as
FieldInfo connectionStringField = GetType().BaseType.GetField("_sqlConnectionString", BindingFlags.Instance | BindingFlags.NonPublic);
connectionStringField.SetValue(this, connectionString);
but my query is to get current value of connectionstringfied...
i tried the below code as
getvalue(obj ss);
waiting for your valuable esponses
it throws me null values
If connectionStringField has found the field (i.e. it is in the base type and is called "_sqlConnectionString", then it should just be:
string connectionString = (string)connectionStringField.GetValue(this);
?
However, using reflection to talk to non-public fields is... unusual.
public static string GetPropertyValue<T>(this T obj, string parameterName)
{
PropertyInfo[] property = null;
Type typ = obj.GetType();
if (listPropertyInfo.ContainsKey(typ.Name))
{
property = listPropertyInfo[typ.Name];
}
else
{
property = typ.GetProperties();
listPropertyInfo.TryAdd(typ.Name, property);
}
return property.First(p => p.Name == parameterName).GetValue(obj, null).ToString();
}
listPropertyInfo is a cache to avoid reflection performance issue
public static void SetPropertyValue<T>(this T obj, string parameterName, object value)
{
PropertyInfo[] property = null;
Type typ = obj.GetType();
if (listPropertyInfo.ContainsKey(typ.Name))
{
property = listPropertyInfo[typ.Name];
}
else
{
property = typ.GetProperties();
listPropertyInfo.TryAdd(typ.Name, property);
}
if (value == DBNull.Value)
{
value = null;
}
property.First(p => p.Name == parameterName).SetValue(obj,value, null);
}
I used the same trick for setters
From the world of PHP I have decided to give C# a go. I've had a search but can't seem to find the answer of how to do the equivalent to this.
$object = new Object();
$vars = get_class_vars(get_class($object));
foreach($vars as $var)
{
doSomething($object->$var);
}
I basically have a List of an object. The object could be one of three different types and will have a set of public properties. I want to be able to get a list of the properties for the object, loop over them and then write them out to a file.
I'm thinking this has something to do with c# reflection but it's all new to me.
Any help would be greatly appreciated.
This should do it:
Type myType = myObject.GetType();
IList<PropertyInfo> props = new List<PropertyInfo>(myType.GetProperties());
foreach (PropertyInfo prop in props)
{
object propValue = prop.GetValue(myObject, null);
// Do something with propValue
}
void Test(){
var obj = new{a="aaa", b="bbb"};
var val_a = obj.GetValObjDy("a"); //="aaa"
var val_b = obj.GetValObjDy("b"); //="bbb"
}
//create in a static class
static public object GetValObjDy(this object obj, string propertyName)
{
return obj.GetType().GetProperty(propertyName).GetValue(obj, null);
}
Yes, Reflection would be the way to go. First, you would get the Type that represents the type (at runtime) of the instance in the list. You can do this by calling the GetType method on Object. Because it is on the Object class, it's callable by every object in .NET, as all types derive from Object (well, technically, not everything, but that's not important here).
Once you have the Type instance, you can call the GetProperties method to get the PropertyInfo instances which represent the run-time informationa about the properties on the Type.
Note, you can use the overloads of GetProperties to help classify which properties you retrieve.
From there, you would just write the information out to a file.
Your code above, translated, would be:
// The instance, it can be of any type.
object o = <some object>;
// Get the type.
Type type = o.GetType();
// Get all public instance properties.
// Use the override if you want to classify
// which properties to return.
foreach (PropertyInfo info in type.GetProperties())
{
// Do something with the property info.
DoSomething(info);
}
Note that if you want method information or field information, you would have to call the one of the overloads of the GetMethods or GetFields methods respectively.
Also note, it's one thing to list out the members to a file, but you shouldn't use this information to drive logic based on property sets.
Assuming you have control over the implementations of the types, you should derive from a common base class or implement a common interface and make the calls on those (you can use the as or is operator to help determine which base class/interface you are working with at runtime).
However, if you don't control these type definitions and have to drive logic based on pattern matching, then that's fine.
well, in C# it's similar.
Here's one of the simplest examples (only for public properties):
var someObject = new { .../*properties*/... };
var propertyInfos = someObject.GetType().GetProperties();
foreach (PropertyInfo pInfo in propertyInfos)
{
string propertyName = pInfo.Name; //gets the name of the property
doSomething(pInfo.GetValue(someObject,null));
}
One line solution using Linq...
var obj = new {Property1 = 1, Property2 = 2};
var property1 = obj.GetType().GetProperties().First(o => o.Name == "Property1").GetValue(obj , null);
To get specific property value from property name
public class Bike{
public string Name {get;set;}
}
Bike b = new Bike {Name = "MyBike"};
to access property value of Name from string name of property
public object GetPropertyValue(string propertyName)
{
//returns value of property Name
return this.GetType().GetProperty(propertyName).GetValue(this, null);
}
You can use GetType - GetProperties - Linq Foreach:
obj.GetType().GetProperties().ToList().ForEach(p =>{
//p is each PropertyInfo
DoSomething(p);
});
Here's something I use to transform an IEnumerable<T> into a DataTable that contains columns representing T's properties, with one row for each item in the IEnumerable:
public static DataTable ToDataTable<T>(IEnumerable<T> items)
{
var table = CreateDataTableForPropertiesOfType<T>();
PropertyInfo[] piT = typeof(T).GetProperties();
foreach (var item in items)
{
var dr = table.NewRow();
for (int property = 0; property < table.Columns.Count; property++)
{
if (piT[property].CanRead)
{
var value = piT[property].GetValue(item, null);
if (piT[property].PropertyType.IsGenericType)
{
if (value == null)
{
dr[property] = DBNull.Value;
}
else
{
dr[property] = piT[property].GetValue(item, null);
}
}
else
{
dr[property] = piT[property].GetValue(item, null);
}
}
}
table.Rows.Add(dr);
}
return table;
}
public static DataTable CreateDataTableForPropertiesOfType<T>()
{
DataTable dt = new DataTable();
PropertyInfo[] piT = typeof(T).GetProperties();
foreach (PropertyInfo pi in piT)
{
Type propertyType = null;
if (pi.PropertyType.IsGenericType)
{
propertyType = pi.PropertyType.GetGenericArguments()[0];
}
else
{
propertyType = pi.PropertyType;
}
DataColumn dc = new DataColumn(pi.Name, propertyType);
if (pi.CanRead)
{
dt.Columns.Add(dc);
}
}
return dt;
}
This is "somewhat" overcomplicated, but it's actually quite good for seeing what the outcome is, as you can give it a List<T> of, for example:
public class Car
{
string Make { get; set; }
int YearOfManufacture {get; set; }
}
And you'll be returned a DataTable with the structure:
Make (string)
YearOfManufacture (int)
With one row per item in your List<Car>
This example trims all the string properties of an object.
public static void TrimModelProperties(Type type, object obj)
{
var propertyInfoArray = type.GetProperties(
BindingFlags.Public |
BindingFlags.Instance);
foreach (var propertyInfo in propertyInfoArray)
{
var propValue = propertyInfo.GetValue(obj, null);
if (propValue == null)
continue;
if (propValue.GetType().Name == "String")
propertyInfo.SetValue(
obj,
((string)propValue).Trim(),
null);
}
}
I haven't found this to work on, say Application objects. I have however had success with
var serializer = new System.Web.Script.Serialization.JavaScriptSerializer();
string rval = serializer.Serialize(myAppObj);
You can try this:
string[] arr = ((IEnumerable)obj).Cast<object>()
.Select(x => x.ToString())
.ToArray();
Once every array implements IEnumerable interface
public Dictionary<string, string> ToDictionary(object obj)
{
Dictionary<string, string> dictionary = new Dictionary<string, string>();
Type objectType = obj.GetType();
IList<PropertyInfo> props = new List<PropertyInfo>(objectType.GetProperties());
foreach (PropertyInfo prop in props)
{
object propValue = prop.GetValue(obj, null);
dictionary.Add(prop.Name, propValue.ToString());
}
return dictionary;
}
/// get set value field in object to object new (two object field like )
public static void SetValueObjectToObject (object sourceObj , object resultObj)
{
IList<PropertyInfo> props = new List<PropertyInfo>(sourceObj.GetType().GetProperties());
foreach (PropertyInfo prop in props)
{
try
{
//get value in sourceObj
object propValue = prop.GetValue(sourceObj, null);
//set value in resultObj
PropertyInfo propResult = resultObj.GetType().GetProperty(prop.Name, BindingFlags.Public | BindingFlags.Instance);
if (propResult != null && propResult.CanWrite)
{
propResult.SetValue(resultObj, propValue, null);
}
}
catch (Exception ex)
{
// do something with Ex
}
}
}
Here is the Context :
I try to code a mapper for converting my DomainModel Objects to ViewModel Ojects dynamically. The problem I get, it's when I try to invoke a method of generic class by reflection I get this error :
System.InvalidOperationException : Late bound operations cannot be performed on types or methods for which ContainsGenericParameters is true.
Can someone help-me to figure out where is the fault ? It would be greatly appreciated
Here is the Code (I tried to simplified it) :
public class MapClass<SourceType, DestinationType>
{
public string Test()
{
return test
}
public void MapClassReflection(SourceType source, ref DestinationType destination)
{
Type sourceType = source.GetType();
Type destinationType = destination.GetType();
foreach (PropertyInfo sourceProperty in sourceType.GetProperties())
{
string destinationPropertyName = LookupForPropertyInDestinationType(sourceProperty.Name, destinationType);
if (destinationPropertyName != null)
{
PropertyInfo destinationProperty = destinationType.GetProperty(destinationPropertyName);
if (destinationProperty.PropertyType == sourceProperty.PropertyType)
{
destinationProperty.SetValue(destination, sourceProperty.GetValue(source, null), null);
}
else
{
Type d1 = typeof(MapClass<,>);
Type[] typeArgs = { destinationProperty.GetType(), sourceType.GetType() };
Type constructed = d1.MakeGenericType(typeArgs);
object o = Activator.CreateInstance(constructed, null);
MethodInfo theMethod = d1.GetMethod("Test");
string toto = (string)theMethod.Invoke(o,null);
}
}
}
}
private string LookupForPropertyInDestinationType(string sourcePropertyName, Type destinationType)
{
foreach (PropertyInfo property in destinationType.GetProperties())
{
if (property.Name == sourcePropertyName)
{
return sourcePropertyName;
}
}
return null;
}
}
You need to call GetMethod on the constructed type constructed, not on the type definition d1.
// ...
Type d1 = typeof(MapClass<,>);
Type[] typeArgs = { destinationProperty.GetType(), sourceType.GetType() };
Type constructed = d1.MakeGenericType(typeArgs);
object o = Activator.CreateInstance(constructed, null);
MethodInfo theMethod = constructed.GetMethod("Test");
string toto = (string)theMethod.Invoke(o, null);
// ...