I want to implement a custom collection that contains instances of my class.
This is my class, a bit simplified here.
public class Property : IComparable<Property>
{
public string Name;
public string Value;
public string Group;
public string Id;
...
...
public int CompareTo(Property other)
{
return Name.CompareTo(other.Name);
}
}
I am adding instances of Property to a List collection
Public List<Property> properties;
I can iterate through properties or access a specific property through the index position.
I want to however be able to access the property by its Name such that
var myColor = properties["Color"].Value;
and I do not have an efficient way to do this. I assume that properties should be written as a custom list collection class to achieve this. Does anyone have a code sample I can look at?
Thanks for the help.
Easiest methods were already mentioned, but I see two:
Method 1
Convert to dictionary and lookup there.
var props = properties.ToDictionary( x => x.Name );
Property prop = props["some name"];
Method 2
Create your own collection type which would support indexing
by your arbitrary type.
public class PropertyCollection : List<Property>
{
public Property this[string name]
{
get
{
foreach (Property prop in this)
{
if (prop.Name == name)
return prop;
}
return null;
}
}
}
and use this collection instead
PropertyCollection col = new PropertyCollection();
col.Add(new Property(...));
Property prop = col["some name"];
You can use a Dictionary:
Dictionary<string, Property> properties = new Dictionary<string, Property>();
//you add it like that:
properties[prop.Name] = prop;
//then access it like that:
var myColor = properties["Color"];
Use a Dictionary<string,Property> for this purpose. The key will be the property name and the value will be the Property instance itself.
Related
Struggled to come up with a decent way to ask/title this question, but will try and illustrate it as best I can.
I am working with a data structure something like this:
public Foo
{
public Bar Bar {get;set;}
}
public Bar
{
public SubTypeA TypeA {get;set;}
public SubTypeB TypeB {get;set;}
...
}
public SubTypeA
{
public int Status {get;set;}
...
}
Note that I am unable to change the data structure for this.
There are many different types in the Bar class, which all have different properties within them, but common to all of them is the property of Status.
What I need to do, is given an object of type Foo, is record the statuses for every item in the Bar object within it. Not every SubType is going to have a value every time though, some could be null.
I can sort of manage it by using a recursive function like below to loop through all the properties. It isn't ideal though I don't think as the loop could get quite large as there could be a lot of properties on each SubType.
private void GetProperties(Type classType, object instance)
{
foreach (PropertyInfo property in classType.GetProperties())
{
object value = property.GetValue(instance, null);
if (value != null)
{
if (property.Name == "Status")
{
Record(classType, value);
}
GetProperties(property.PropertyType, value);
}
}
}
Is this about the only approach that there is for such a problem?
EDIT: Going by the answer given by Selman22, I have come up with another issue wherein I am trying to create an anonymous object based on the status and name of object.
var z = instance.GetType()
.GetProperties()
.Select(x => new
{
status = x.GetValue(instance).GetType().GetProperty("status").GetValue(x, null),
name = x.Name
})
.ToList();
This is throwing an error of Object does not match target type. when trying to retrieve the value. Is this possible in a 1 liner?
Type class contains GetProperty(string name, BindingFlags method) that you can use to retrieve specific property. Instead of looping through every property use this method.
http://msdn.microsoft.com/en-us/library/system.type.getproperty(v=vs.110).aspx
// Get Type object of MyClass.
Type myType=typeof(MyClass);
// Get the PropertyInfo by passing the property name and specifying the BindingFlags.
PropertyInfo myPropInfo = myType.GetProperty("MyProperty", BindingFlags.Public | BindingFlags.Instance);
You can get all Status properties using LINQ instead of recursion:
var barInstance = typeof(Foo).GetProperty("Bar").GetValue(fooInstance);
var statusProperties = barInstance.GetType()
.GetProperties()
.Select(x => x.GetValue(barInstance).GetType().GetProperty("Status"));
I have a parent class, with has a property that contain a list of the children class.
//Parent Class :
public class Family
{
private MemberList _familymember = new MemberList();
public MemberList FamilyMember
{
get { return _familymember ; }
set { _familymember = value; }
}
}
//Children Class :
public class Member
{
private string _name;
public string Name
{
get { return _name; }
set { _name= value; }
}
}
public class MemberList : List<Member>
{
}
//And here is how I use them :
Family myFamily = new Family();
Member dad = new Member();
Member mom = new Member();
//I add the children into the parent property :
myFamily.FamilyMember.Add(dad);
myFamily.FamilyMember.Add(mom);
//I have other different similar parent-children classes, eg : School-Classroom, Country-States and etc.
//In one condition, I need to write a generic class to process the List<custom> type property :
//some function that will return a children list object.
object ChildrenList = somefunction();
List<object> ObjectList = (List<object>)ChildrenList;
//and trying to get the Type name inside the List :
Type ChildrenType = ObjectList.GetType().GetGenericArguments()[0];
//It gives me error :
Unable to cast object of type 'MemberList' to type'System.Collections.Generic.List`1[System.Object]'
//or when I process other types of list
Unable to cast object of type 'ClassroomList' to type 'System.Collections.Generic.List`1[System.Object]'
Unable to cast object of type 'StateList' to type 'System.Collections.Generic.List`1[System.Object]'
I am trying to write a general class to process the list, therefore I wouldn't know the type of the list property. Any idea how I can cast them into List ?
All help is appreciated !!
Update & Solution :
Like aevitas suggested, I have changed the list property to use
private List<Member> _familymember = List<Member>();
And then I use the IList interface I found in other topic,
object returnedList = somefunction(); // some function that return a List of object.
IList myList = (IList)returnedList ;
Now the rest of the code works, I can loop through the IList like normal.
Thanks all!
You could add a common interface for all children classes (Member, Classroom & State), otherwise there is absolutely no connection between List<object> and List<MemberType> as this would allow the following:
List<object> myList = (List<object>) memberTypeList;
myList.Add(5); // oops
Also: as unfortunate as it is, there is no type aliasing in C# like there is in C or C++. Still, deriving from a class looks like its a workaround but its semantically wrong and should not be used.
You should never inherit List<T>, instead you should declare one instead:
public class Family
{
private List<Member> _familymember = List<Member>();
public List<Member> FamilyMember
{
get { return _familymember ; }
set { _familymember = value; }
}
}
On top of that, you should never be casting your types down to object, use a common type or interface instead as D.R. suggested.
If you want to stick to your guns, and you want to convert your MemberList object to a List<Member>, you could do it like this:
public static IEnumerable<T> GetList<T>(List<T> source)
{
return source;
}
And call it like this:
var list = GetList<Member>(memberList).ToList();
That should at least solve your compiler errors, but it won't solve the design errors in this one.
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've some classes and want to access their properties using index or something like
ClassObject[0] or better will be ClassObject["PropName"]
instead of this
ClassObj.PropName.
Thanks
You need indexers:
http://msdn.microsoft.com/en-us/library/aa288465(v=vs.71).aspx
public class MyClass
{
private Dictionary<string, object> _innerDictionary = new Dictionary<string, object>();
public object this[string key]
{
get { return _innerDictionary[key]; }
set { _innerDictionary[key] = value; }
}
}
// Usage
MyClass c = new MyClass();
c["Something"] = new object();
This is notepad coding, so take it with a pinch of salt, however the indexer syntax is correct.
If you want to use this so you can dynamically access properties, then your indexer could use Reflection to take the key name as a property name.
Alternatively, look into dynamic objects, specifically the ExpandoObject, which can be cast to an IDictionary in order to access members based on literal string names.
You can do something like this, a pseudocode:
public class MyClass
{
public object this[string PropertyName]
{
get
{
Type myType = typeof(MyClass);
System.Reflection.PropertyInfo pi = myType.GetProperty(PropertyName);
return pi.GetValue(this, null); //not indexed property!
}
set
{
Type myType = typeof(MyClass);
System.Reflection.PropertyInfo pi = myType.GetProperty(PropertyName);
pi.SetValue(this, value, null); //not indexed property!
}
}
}
and after use it like
MyClass cl = new MyClass();
cl["MyClassProperty"] = "cool";
Note that this is not complete solution, as you need to "play" with BindingFlags during reflection access if you want to have non public properties/fields, static ones and so on.
public string this[int index]
{
get
{ ... }
set
{ ... }
}
This will give you an indexed property. You can set any parameter you wish.
I'm not sure what you mean here, but I'll say that you have to make ClassObject some sort of IEnumirable type, like List<> or Dictionary<> to use it the way to aim for here.
I have a class called Prescriptions. It has properties that are other classes. So, for example, a property name of Fills would be from the PDInt class which has other properties about the value that I need.
If I want to set the value of the Fills property in the Prescription class it would be something like
Prescription p = new Prescription();
p.Fills.Value = 33;
So now I want to take the name of the Fills property and stuff it in a the tag property in a winform control.
this.txtFills.Tag = p.Fills.GetType().Name;
However when I do this, I get the base class of the property, not the property name. So instead of getting "Fills", I get "PDInt".
How do I get the instantiated name of the property?
Thank you.
Below is an extension method that I use it when I wanna work like you:
public static class ModelHelper
{
public static string Item<T>(this T obj, Expression<Func<T, object>> expression)
{
if (expression.Body is MemberExpression)
{
return ((MemberExpression)(expression.Body)).Member.Name;
}
if (expression.Body is UnaryExpression)
{
return ((MemberExpression)((UnaryExpression)(expression.Body)).Operand)
.Member.Name;
}
throw new InvalidOperationException();
}
}
use it as :
var name = p.Item(x=>x.Fills);
For detail about how method works see Expression Tree in .Net
Check this blogpost which is helpful : http://handcraftsman.wordpress.com/2008/11/11/how-to-get-c-property-names-without-magic-strings/
Do this you need to make USe of reflection feature of .net framework.
Something like this
Type type = test.GetType();
PropertyInfo[] propInfos = type.GetProperties();
for (int i = 0; i < propInfos.Length; i++)
{
PropertyInfo pi = (PropertyInfo)propInfos.GetValue(i);
string propName = pi.Name;
}
You can get you like as this ? ↓
public class Prescription
{
public PDInt Fills;
}
public class PDInt
{
public int Value;
}
Prescription p = new Prescription();
foreach(var x in p.GetType().GetFields())
{
// var type = x.GetType(); // PDInt or X //Fills
}