Casting to object from a collection vs using foreach - c#

I am a little confused about the difference between the two statements.
This one works and prints out the property results just fine.
foreach( string key in result.Properties.PropertyNames )
{
foreach( object property in result.Properties[key] )
{
Console.WriteLine("{0}:{1}", key, property.ToString());
}
}
and the following doesn't work. I thought by casting the specific property to object it would be the same thing but its obviously not:
foreach( string key in result.Properties.PropertyNames )
{
if( key == "name" )
{
Console.WriteLine("{0}:{1}", key, ((object)(result.Properties[key])).ToString() );
}
}
I get the object type of result.Properties[key] printed instead.

The two snippets do completely different things.
In the first example, result.Properties[key] is a collection of some sort (IEnumerable). It's looping through each object in the collection and printing the string representation of that object (ToString()) out to the screen.
In the second example, it's just printing the string representation of the collection itself, which often times is just the name of the type.
NOTE: You would almost never want ToString contain out the contents of the collection.

The difference is, even though property is type object in the first example, The actual property still has an underlying type that is being used for ToString(). You're just using the object type to hold a more derived type.
In the second example, where you cast it to type object you're telling the compiler "I don't care what type the property actually is, treat it like an object so it ends up using object.ToString() instead of property.ToString().

Properties is an object ith property names and per property name a set of values.
So... result.Properties[key] will return a ResultPropertyValueCollection, which contains one or more values.
You can enumerate through these values which you do in your first piece of code.
In your second example, your want to show all values in this collection. This can easily be done with LINQ. I split up the code for better understanding, but it can be written in one line:
foreach( string key in result.Properties.PropertyNames )
{
if( key == "name" )
{
IEnumerable collection = result.Properties[key];
string[] seperateValues = collection.Cast<object>().Select(o => o.ToString()).ToArray();
string joinedValues = String.Join(", ", seperateValues)
Console.WriteLine("{0}:{1}", key, joinedValues);
}
}

Related

Object does not match target type PropertyInfo SetValue - one class to another

So I have 2 classes, both have identical Property names. One class contains different variables: int, strings, bool and DateTime The second class contains only 1 int and the rest are all strings.
Now I want to loop through all the properties, get the value from class1, encrypt that data and save it as a string in obj2, then return it to the main form (to save it in a database later).
public PersoonEncrypted EncryptPersonClass(Class1 object1)
{
PersoonEncrypted persEncrypt = new PersoonEncrypted(); //second class obj
Type type = object1.GetType();
PropertyInfo[] properties = type.GetProperties();
Type type2 = persEncrypt.GetType();
PropertyInfo[] properties2 = type.GetProperties();
foreach (var bothProperties in properties.Zip(properties2, (obj1, obj2) => new { Obj1 = obj1, Obj2 = obj2 }))
{
string value = "";
value = bothProperties.Obj1.GetValue(object1) as string;
if (!string.IsNullOrWhiteSpace(value))
{
string encryptValue = Encrypt(value);
if ((bothProperties.Obj2 != null) && (bothProperties.Obj2.PropertyType == typeof(string)))
{ //!= null check has no effect at all
bothProperties.Obj2.SetValue(persEncrypt, encryptValue, null); //errorLine
}
}
}
return persEncrypt;
}
That is what I came up with until now.
I have, of course, searched for other solutions like this one. This, after applying some own changes, didn't return any errors, but it didn't save any encrypted strings into the class persEncrypt. What I concluded was, from that test, is that it was testing if the value in the second class(persEncrypt in my example) from the particular property was null, while it shouldn't do that, it should make a new instance of that variable and save it in the object class, but removing that check gave me the same error.
you're just .Zip-ing the two lists of PropertyInfo objects, which simply iterates through both lists and doesn't check or sort for any sort of matching. This could result in erroneous behavior depending on the order in which properties appear - consider using a .Join instead to match property names.
This code doesn't check for an indexer on the property before attempting to assign to it without one - any indexed property which is of type string will make it to this point and then throw an exception when you try to set it.
Because this code is calling into Properties, there's the possibility an exception is being thrown by the code of the Property itself. This is where a StackTrace from your exception could reveal much more about what's happening.
Your code also checks for a property of type string directly - when using reflection you should use IsAssignableFrom instead in order to allow for inherited types, though that is unlikely the issue in this one case.

Calling the property names of a dynamic property "ints": can't call property

I am assigning property names of a dynamic object as ints in string form. The int value represents an int ID in a database I am using. However I am stuck on how to retrieve the value assigned to the property as shown below:
dynamic test = new ExpandoObject()
IDictionary<string, object> proxyFiler = test as IDictionary<string, object>;
proxyFiler["four"] = 4;
proxyFiler["5"] = 5;
int r = test.four; // Works
int s = test.5; // Doesn't work
A method which reads the database will return an "int" and I would like to be able to access the property value with that property name.
To expand on this: what if I wanted to do a linq query to sort out a list of dynamic objects according to a property name? In this case I need to get the propertyName which I have retrieved as a string e.g. "15":
return disorderedList.OrderBy(o => o.propertyName).ToList();
Does anyone know a simple solution to this problem or do you recommend a different approach? Thanks.
In order for dynamic to work in this way, the key has to follow the rules for valid identifier names in C# (the rules are specified in this outdated MSDN page, but also in the C# language specification). A single number (5) is not an allowed identifier name, which is why that doesn't work.
Note that you can still retrieve the value by using the type as a dictionary, in a similar manner to how you populated it.
As for your second example - you are never using value, so it has no effect. It's the same as just writing int r = test.four;
Edit:
I believe, given your approach, you'd need to cast to a dictionary:
return disorderedList
.OrderBy(o => ((IDictionary<string, object>)o)[propertyName]).ToList();

Get Types using IEnumerable GetGenericArguments

I have developed a MVC helper for generating display and editable tables (a jquery plugin is required to allow dynamic addition and deletion of rows with full postback in the editable tables) e.g.
#Htm.TableDisplayFor(m => m.MyCollection as ICollection)
which used in conjunction with attributes will include totals in the footer, add columns for view and edit links, render hyperlinks for complex type etc. e.g.
[TableColumn(IncludeTotals = true)]
I'm about to publish it on CodeProject but before doing so, would like to solve one issue.
The helper first gets the ModelMetadata from the expression, checks that it implements ICollection, then gets the type in the collection (note the following snippet is from accepted answers on SO, but as explained below, is not entirely correct)
if (collection.GetType().IsGenericType)
{
Type type = collection.GetType().GetGenericArguments()[0]
The type is used to generate ModelMetadata for the table header (there might not be any rows in the table) and each row in the table body (in case some items are inherited types which have additional properties and would otherwise screw up the column layout)
foreach (var item in collection)
{
ModelMetadata itemMetadata = ModelMetadataProviders.Current
.GetMetadataForType(() => item, type);
What I would like to be able to do is use IEnumerable rather than ICollection so that .ToList() does not need to be called on linq expressions.
In most cases IEnumerable works fine e.g.
IEnumerable items = MyCollection.Where(i => i....);
is OK because .GetGenericArguments() returns an array containing only one type.
The problem is that '.GetGenericArguments()' on some queries returns 2 or more types and there seems to be no logical order. For example
IEnumerable items = MyCollection.OrderBy(i => i...);
returns [0] the type in the collection, and [1] the type used for ordering.
In this case .GetGenericArguments()[0] still works, but
MyCollection.Select(i => new AnotherItem()
{
ID = i.ID,
Name = 1.Name
}
returns [0] the type in the original collection and [1] the type of AnotherItem
So .GetGenericArguments()[1] is what I need to render the table for AnotherItem.
My question is, is there a reliable way using conditional statements to get the type I need to render the table?
From my tests so far, using .GetGenericArguments().Last() works in all cases except when using OrderBy() because the sort key is the last type.
A few things I've tried so far include ignoring types that are value types (as will often be the case with OrderBy(), but OrderBy() queries might use a string (which could be checked) or even worse, a class which overloads ==, < and > operators (in which case I would not be able to tell which is the correct type), and I have been unable to find a way to test if the collection implements IOrderedEnumerable.
Solved (using comments posted by Chris Sinclair)
private static Type GetCollectionType(IEnumerable collection)
{
Type type = collection.GetType();
if (type.IsGenericType)
{
Type[] types = type.GetGenericArguments();
if (types.Length == 1)
{
return types[0];
}
else
{
// Could be null if implements two IEnumerable
return type.GetInterfaces().Where(t => t.IsGenericType)
.Where(t => t.GetGenericTypeDefinition() == typeof(IEnumerable<>))
.SingleOrDefault().GetGenericArguments()[0];
}
}
else if (collection.GetType().IsArray)
{
return type.GetElementType();
}
// TODO: Who knows, but its probably not suitable to render in a table
return null;
}

IEnumerable.GroupJoin and Entity Framework objects

(See this question for a little background on this one.)
I need to match up items in a List<T> collection of Entity records with a List<T> collection of objects (the Entity object collection is of type Citation and the other is of type sRecord). There is a 1-to-1 relationship of sorts between the second collection and the first where each of the objects in the second match up to exactly one record in the first (but not necessarily vice versa). They match on a single field called ID in the Citation class and id in the sRecord class. Trying to run through nested loops to match them up quickly became bogged down, so I sought out a means to match up the entire collections once and then iterate through that matched set.
This is how I put together the suggested group join statement:
var draftMatches = draftRecords.GroupJoin(sRecords,
Citation => Citation.ID,
stiRecord => sRecord.id,
(Citations, sRecords) =>
new
{
Citation = Citations,
sRecord = sRecords.Select(sRecord => sRecord)
});
I don't have much confidence that I got it right.
What I need to do with the resulting matched set is compare fields in the sRecord object to fields in the Citation object to determine whether the Citation object needs to be updated. Since I'm working with Entity, I assumed that I needed to preserve the reference to the Citation object when I updated it in a separate method (separated for code reuse purposes), so this is how I'm trying to do that update:
DateTime recordDate = RecordUtilities.ConvertToDate(match.sRecord.FirstOrDefault().modifiedDate);
int comparison = recordDate.CompareTo((DateTime)match.Citation.modifiedDate);
if (comparison > 0)
{
EntityUtilities.MapToCitation(ref match.Citation, match.sRecord.FirstOrDefault());
updatedDraft++;
}
At this point, I'm getting an IntelliSense error on match.Citation in the call to MapToCitation, stating "A property, indexer, or dynamic member access may not be passed as an out or ref parameter."
I need to ensure that the Citation object that gets saved when I do context.Save() is updated from the sRecord object if appropriate. I also need to make sure that I'm saving time with the matching over the previous code. What do I need to change about my code to achieve these goals?
var draftRecordDic = draftRecords.ToDictionary(record => record.ID);
var sRecordsDic = sRecords.ToDictionary(record => record.ID);
var validKeys = sRecordsDic.Keys.Intersect(draftRecordDic.Keys);
foreach(var key in validKeys)
{
var recOriginal = draftRecordDic[key];
var recMo = sRecordsDic[key];
// do your code here.
}
This should work nicely & is simple to understand.

How do I write a method that can output data from any class using generics?

I'm trying to write a custom method to populate a ListView control using Generics:
private void BindDataToListView(List<T> containerItems)
{
this.View = View.Details;
this.GridLines = true;
this.FullRowSelect = true;
if (this.Items.Count > 0)
this.Items.Clear();
this.BeginUpdate();
int i = 0;
foreach (T item in containerItems)
{
// do something
}
this.EndUpdate();
}
The parameter containerItems can have many items since I'm using generics. But I get stuck in the foreach loop. How do I access the values in containerItems?
Do I have to use reflection on each instance of T in the foreach loop? I think I do to retrieve the property name. But once I have the property name of the type T, how do I retrieve the value?
The most common way of doing this (with winforms) is via TypeDescriptor; this allow you to use things DataTable the same as classes; the "full" pattern is quite complex (and involves checking for IListSource, ITypedList, etc; however, the short version is; to get the available properties:
PropertyDescriptorCollection props = TypeDescriptor.GetProperties(typeof(T));
To get a named property:
PropertDescriptor prop = props[propName];
To get a value for an instance (sourceObject):
object val = prop.GetValue(sourceObject);
To render a value as a string (using the designated converter):
string s = prop.Converter.ConvertToString(val);
You could limit T to an interface, and use that interface in the iteration.
What does T represent ?
Like it is now, it is a generic type and it can be ... anything.
So, what I would do, is create an interface IListViewBindable or something like that. That interface could then have a method 'CreateListViewItem' for instance.
Then, I would change the method, so that a constraint is applied to your type-parameter T, saying that T should implement IListViewBindable, like this:
public void BindDataToListView<T>( List<T> containerItems ) where T : IListViewBindable
{}
In your BindDataToListView method, you could then do this:
foreach( T item in containerItems )
{
this.Items.Add (item.CreateListViewItem());
}
If the items in the list are of totally unconstrained type, then you can treat them as simply of type object. You call GetType() to get the type of the object. On that you can call GetProperties() to get an array of PropertyInfo objects. And on those you can call GetValue() to retrieve the value of the property.
If you already know the name of a property, just call GetProperty() to retrieve it:
string valueAsString = item.GetType().GetProperty("Something")
.GetValue(item, null).ToString();
I don't completely understand what you're asking, but I think that this will point you in the right direction. Please ask for clarification if it looks like it can help and it's unclear.
You can access a given property of an object using reflection via
object o;
PropertyInfo info = o.GetType().GetProperty().GetProperty("NameOfPropertyIWant");
and you can get the value via
object value = info.GetValue(o, null);
Now, if you're going to be accessing a property of the same name on objects of various types, you should consider adding an interface
public interface IHasThePropertyIWant {
object NameOfPropertyIWant { get; }
}
Then you can enforce this via
void BindDataToListView(List<T> containerItems) where T : IHasThePropertyIWant
and use it in the look like so
foreach (T item in containerItems) {
object o = item.NameOfPropertyIWant;
// do something with o
}
ObjectListView uses reflection to do exactly what you are trying to do: populate a ListView using reflection. You could save yourself a lot of trouble by using it. It has already solved all the tricky problems you are going to encounter on this path.
If you really, really want to do it yourself, Marc's answer is (of course) completely correct.

Categories

Resources