This is my code
public virtual T GetRepository<T>()
where T : class
{
var type = typeof ( T );
var properties = this.GetType().GetProperties();
foreach ( var property in properties )
{
var name = property.Name;
if ( name == type.Name )
{
var a = this.GetType().GetProperty(name) as T;
return a;
}
}
return null;
}
The objective is to return the generic type T. First I get the properties of the class, and then I loop over the properties in hopes of finding a property name which has the same name as the type T.
This works okay in my case because the properties are all of the form Interface<FooRepository> FooRepository { get; set; } so I need only compare the two.
This method is inside a class. The problem is that a will change to null immediately after the statement as T. What can be done?
If you want the value of the property you could try using the GetValue method and replace this:
var a = this.GetType().GetProperty(name) as T;
with this:
var a = property.GetValue(this) as T;
Also it is not quite clear what exactly is the purpose of such method but this condition here looks shaky:
if (name == type.Name)
The name of the property should equal the name of the generic type parameter. Is it really what is needed here?
GetProperty method will always return ProprtyInfo type so unless T is PropertyInfo a will be always null.
Related
So I have a class that is a generic and it may need to, inside a method of its own, create an instance of itself with a different kind of generic, whose type is obtained through reflection.
This is important because this Repository maps T to a database table [it's an ORMish I am writing] and if the class that represents T has a collection representing ANOTHER table I need to be able to instance that and pass it to the repository [ala Inception].
I'm providing the method in case it makes it easier to see the problem.
private PropertiesAttributesAndRelatedClasses GetPropertyAndAttributesCollection()
{
// Returns a List of PropertyAndAttributes
var type = typeof(T);
//For type T return an array of PropertyInfo
PropertiesAttributesAndRelatedClasses PAA = new PropertiesAttributesAndRelatedClasses();
//Get our container ready
//Let's loop through all the properties.
PropertyAndAttributes _paa;
foreach(PropertyInfo Property in type.GetProperties())
{
//Create a new instance each time.
_paa = new PropertyAndAttributes();
//Adds the property and generates an internal collection of attributes for it too
_paa.AddProperty(Property);
bool MapPropertyAndAttribute = true;
//This is a class we need to map to another table
if (Property.PropertyType.Namespace == "System.Collections.Generic")
{
PAA.AddRelatedClass(Property);
//var x = Activator.CreateInstance("GenericRepository", Property.GetType().ToString());
}
else
{
foreach(var attr in _paa.Attrs)
{
if (attr is IgnoreProperty)
{
//If we find this attribute it is an override and we ignore this property.
MapPropertyAndAttribute = false;
break;
}
}
}
//Add this to the list.
if (MapPropertyAndAttribute) PAA.AddPaa(_paa);
}
return PAA;
}
So given
GenericRepository<T>, and I want to make a GenericRepository<string type obtained via reflection from the Property> how would I do this?
The line I need to replace with something that WORKS is:
//var x = Activator.CreateInstance("GenericRepository", Property.GetType().ToString());
Thanks.
I think you're looking for the MakeGenericType method:
// Assuming that Property.PropertyType is something like List<T>
Type elementType = Property.PropertyType.GetGenericArguments()[0];
Type repositoryType = typeof(GenericRepository<>).MakeGenericType(elementType);
var repository = Activator.CreateInstance(repositoryType);
Activator.CreateInstance(typeof(GenericRepository<>).MakeGenericType(new Type[] { Property.GetTYpe() }))
Basically, what I want to do is loop over the list of properties on a class, and where they are a particular type I will check a property of that owned property, then set another property to true.
I wrote the code below to illustrate what I want to do, however it will not compile because it says cannot convert type System.Reflection.PropertyInfo to type ThermodynamicState. In addition it gives the warning that the given expression is never of ThermodynamicState type.
How do I convert each property in the list of properties reflection gives into an object of that type?
public void BasisChanged()
{
foreach (ThermodynamicProperty thermoProperty in this.GetType().GetProperties().Where(p => p.PropertyType is ThermodynamicProperty && !((ThermodynamicProperty)p.PropertyType).IsBasis))
{
thermoProperty.BasisChanged = true;
}
}
I believe, you are trying something like the following:
public void BasisChanged()
{
foreach (var p in GetType().GetProperties().Where(p => typeof(ThermodynamicProperty).IsAssignableFrom(p.PropertyType)))
{
var value = (ThermodynamicProperty)p.GetValue(this);
if (!value.IsBasis)
value.BasisChanged = true;
}
}
You have to check the property's type via PropertyType. IsAssignableFrom() checks if objects of this type can be assigned to a ThermodynamicProperty variable. Then you get the current object with GetValue() and set the BasisChanged property.
Note that this assumes ThermodynamicProperty to be a reference type. If it is a value type, you have to set it back after changing it.
foreach (PropertyInfo p in this.GetType().GetProperties().Where(p => p.PropertyType == typeof(ThermodynamicProperty)))
{
ThermodynamicProperty propertyObject = (ThermodynamicProperty)p.GetValue(this);
if (!propertyObject.IsBasis)
{
propertyObject.BasisChanged = true;
}
}
I have a List that I am iterating through.
Inside the List<> are Argument classes which contain two properties 'PropertyName' and 'Value'
What I need to do is iterate through the collection of Arguments and assign the Value of that Argument to the Property (with the same name as current Argument) of a different class.
Example:
Argument:
PropertyName: ClientID
Value: 1234
Members Class:
ClientID = {Argument Value here}
I hope this makes sense. I have a way of doing it, hard coding the properties of my class and matching it up with the Argument list.
Something like:
foreach(var arg in List<Argument>)
{
Members.ClientID = arg.Find(x => compareName(x, "ClientID")).Value;
//where compareName just does a simple string.Compare
}
But what would the BEST way be for something like this?
EDIT: Sorry about this guys and thanks for the replies so far. Here is what I didn't mention and might make a difference.
Each argument is a different property for the same class. I am iterating through the List and each one in there will be for the same Members class I have to populate.
I wanted to mention this because im thinking in the foreach I might have to use a switch to determine what 'PropertyName' I have for that Argument. ClientID is one of them but I believe there are 14 total properties in the Members class that need populated from the Collection.
Does that change things?
Thanks again
public object this[string propertyName]
{
get
{
Type myType = typeof(UserConfiguration);
PropertyInfo myPropInfo = myType.GetProperty(propertyName);
return myPropInfo.GetValue(this, null);
}
set
{
Type myType = typeof(UserConfiguration);
PropertyInfo myPropInfo = myType.GetProperty(propertyName);
myPropInfo.SetValue(this, value, null);
}
}
Then you can get/set properties within the class using
myClassInstance["ClientId"] = myValue;
If I understand what you're asking, perhaps something like this will work for you:
var argDict = arguments.ToDictionary(x => x.PropertyName, x => x.Value);
Members.ClientID = argDict["ClientID"];
...
If you need to do some special comparison on the keys you can provide the dictionary it's own IEqualityComparer. For example, this will make sure that the keys are treated case-insensitively:
var argDict = arguments.ToDictionary(x => x.PropertyName, x => x.Value,
StringComparer.OrdinalIgnoreCase);
This will work fine as long as the arguments list contains all the values you need. If some arguments might be missing, you'd have to do something like this:
if (argDict.ContainsKey("ClientID")) {
Members.ClientID = argDict["ClientID"];
}
Or possibly something like this:
Members.ClientID = argDict.ContainsKey("ClientID") ? argDict["ClientID"] : "DefaultValue";
I think that your basic intent is to set the value of a property on a target object based on the property name. Since you did not provide the Argument class I will assume it is defined like this:
public class Argument
{
public string PropertyName{get; set;}
public object PropertyValue{get;set;}
}
Further assume you have the class Blah defined like this:
public class Blah
{
public string AString{get; set;}
public int AnInt{get; set;}
public DirectoryInfo ADirInfo{get; set;}
}
If you wish to assign to the properties of a Blah object based on the values in List<Argument> you can do so like this:
List<Argument> arguments = new List<Argument>
{
new Argument(){PropertyName = "AString", PropertyValue = "this is a string"},
new Argument(){PropertyName = "AnInt", PropertyValue = 1729},
new Argument(){PropertyName = "ADirInfo", PropertyValue = new DirectoryInfo(#"c:\logs")}
};
Blah b = new Blah();
Type blahType = b.GetType();
foreach(Argument arg in arguments)
{
PropertyInfo prop = blahType.GetProperty(arg.PropertyName);
// If prop == null then GetProperty() couldn't find a property by that name. Either it doesn't exist, it's not public, or it's defined on a parent class
if(prop != null)
{
prop.SetValue(b, arg.PropertyValue);
}
}
This depends on the objects stored in Argument.PropertyValue having the same type as the property of Blah referred to by Argument.PropertyName (or there must be an implicit type conversion available). For example, if you alter the List<Argument> as follows:
List<Argument> arguments = new List<Argument>
{
new Argument(){PropertyName = "AString", PropertyValue = "this is a string"},
new Argument(){PropertyName = "AnInt", PropertyValue = 1729},
new Argument(){PropertyName = "ADirInfo", PropertyValue = "foo"}
};
you will now get an exception when attempting to assign to Blah.ADirInfo: Object of type 'System.String' cannot be converted to type 'System.IO.DirectoryInfo'
I have a class which uses generic properties. For example:
class Person
{
public MyGenericProperty<string> Field1
{
get { return field1; }
set { field1 = value; }
}
private MyGenericProperty<string> field1= new MyInheritedGenericProperty<string>("Alan1");
}
I want to use this class with reflection at another class and i have a method like that
public void DoSomethingWithProperty(object sourceobject)
{
foreach (var aProperty in sourceobject.GetType().GetProperties())
{
*if(aProperty.PropertyType == typeof(MyGenericProperty<>))*
{
*var obj = (MyGenericProperty<>)aProperty.GetValue(sourceobject, null);*
}
}
return null;
}
I have two problem
1- How can do type check of generic property. In that example code of if(aProperty.PropertyType == typeof(MyGenericProperty<>)) does not work.
2- T of MyGenericProperty could be any class and how can cast MyGenericProperty class without knowing T by reflection as
var obj = (MyGenericProperty<>)aProperty.GetValue(sourceobject, null);
Thank for helps.
Firstly, it's important to understand that you don't have a "generic property" - there's no such thing. You have a property whose type is a generic type... and that's not the same thing. (Compare that with a generic type or a generic method, each of which is genuinely generic in terms of introducing new type parameters.)
You can test it using this code:
if (aProperty.PropertyType.IsGenericType &&
aProperty.GetGenericTypeDefinition() == typeof(MyGenericProperty<>))
But as for the casting - it depends on what you want to do with the value afterwards. You may want to declare a non-generic base type of MyGenericProperty<> containing all the members which don't depend on the type parameter. I'd typically give that the same name as the generic type (e.g. MyGenericProperty) just without giving it type parameters. Then if you only need one of those members, you can use:
if (aProperty.PropertyType.IsGenericType &&
aProperty.GetGenericTypeDefinition() == typeof(MyGenericProperty<>))
{
var value = (MyGenericProperty) aProperty.GetValue(sourceObject, null);
// Use value
}
But then in that case you could use Type.IsAssignableFrom anyway:
if (typeof(MyGenericProperty).IsAssignableFrom(aProperty.PropertyType))
{
var value = (MyGenericProperty) aProperty.GetValue(sourceObject, null);
// Use value
}
If these hints don't help you, please give more details of what you're trying to do.
Ok, so I'm learning about generics and I'm trying to make this thing run, but its keep saying me the same error. Here's the code:
public static T Test<T>(MyClass myClass) where T : MyClass2
{
var result = default(T);
var resultType = typeof(T);
var fromClass = myClass.GetType();
var toProperties = resultType.GetProperties();
foreach (var propertyInfo in toProperties)
{
var fromProperty = fromClass.GetProperty(propertyInfo.Name);
if (fromProperty != null)
propertyInfo.SetValue(result, fromProperty, null );
}
return result;
}
This happens because default(T) returns null because T represents a reference type. Default values for reference types are null.
You could change your method to:
public static T Test<T>(MyClass myClass) where T : MyClass2, new()
{
var result = new T();
...
}
and then it will work as you want it to. Of course, MyClass2 and its descendants must have a parameterless constructor now.
The problem here is that T derives from MyClass and is hence a reference type. So the expression default(T) will return the value null. The following call to SetValue is operating an a null value but the property is an instance property hence you get the specified message.
You'll need to do one of the following
Pass a real instance of T to the Test function to set the property values on
Only set the static properties on the type
Instead of
propertyInfo.SetValue(result, fromProperty, null);
try:
foreach (var propertyInfo in toProperties)
{
propertyInfo.GetSetMethod().Invoke(MyClass2, new object[]
{
MyClass.GetType().GetProperty(propertyInfo.Name).
GetGetMethod().Invoke(MyClass, null)
});
}