I am writing some validation code. The code will take data passed into a web service and decide whether it can do the action, or return a message to the caller that they have missed out some fields etc.
I have it mostly working except for arrays. I am marking up the properties with a [RequiredField] attribute to represent fields that are required. So if this is some of my data,
public enum EnumTest
{
Value1,
Value2
}
[DataContract]
public class DummyWebserviceData
{
[DataMember]
[RequiredField]
public EnumTest[] EnumTest{ get; set; }
[DataMember]
[RequiredField]
public DummyWebserviceData2[] ArrayOfData { get; set; }
}
[DataContract]
public class DummyWebserviceData2
{
[DataMember]
[RequiredField]
public string FirstName { get; set;}
[DataMember]
[RequiredField]
public string LastName { get; set;}
[DataMember]
public string Description { get; set;}
}
So what do I have working? I have validation of dates, and strings working. It uses recursion to go any level deep required on the data.
But ... so what about the two arrays there. The first is an array of enums. I want to check in that case that the array is not empty.
The second is the array of DummyWebserviceData2 values. I need to pull each value out and have a look at it individually.
To simplify the code I have written it looks something like this,
foreach (PropertyInfo propertyInfo in data.GetType().GetProperties())
{
if (propertyInfo.PropertyType.IsArray)
{
// this craps out
object[] array = (object[])propertyInfo.GetValue(data, new object[] { 0 });
}
}
So it seems to me that the first thing is that I can tell it is an array. But how then can I tell how many items are in the array?
At runtime the object will have been dynamically subclassed from the Array data type (this MSDN topic details that), therefore you don't need to reflect into the array, you can cast the object to Array, and then use the Array.GetValue instance method:
Array a = (Array)propertyInfo.GetValue(data);
for(int i = 0; i< a.Length; i++)
{
object o = a.GetValue(i);
}
You can also iterate over an array as well - since from .Net 2.0 onwards:
In the .NET Framework version 2.0, the Array class implements the System.Collections.Generic::IList, System.Collections.Generic::ICollection, and System.Collections.Generic::IEnumerable generic interfaces.
You don't need to know the T, since from these you can get an IEnumerable; which you can then use a Cast() operation on, or indeed just work at the object level.
Incidentally, the reason why your code isn't working is because you can't cast an array of MyType[] to object[] because object[] is not a base type of MyType[] - only object is.
This method works rather well, and it's simple code.
var array = ((IEnumerable)propertyInfo.GetValue(instance)).Cast<object>().ToArray();
foreach (PropertyInfo propertyInfo in data.GetType().GetProperties())
{
if (propertyInfo.PropertyType.IsArray)
{
// first get the array
object[] array = (object[])propertyInfo.GetValue(data)
// then find the length
int arrayLength = array.GetLength(0);
// now check if the length is > 0
}
}
The answer with the array is nice, but as mentioned it does not work for some other collection types. If you don't know what type your collection is of try something like:
IEnumerable<object> a = (IEnumerable<object>)myPropInfo.GetValue(myResourceObject);
// at least foreach now is available
foreach (object o in a)
{
// get the value
string valueAsString = o.ToString();
}
Related
I'm trying to make a text writer according to my classes properties in the following pattern:
MyClass
ID 1
Name MyName
AnotherProperty SomeValue
ThisIsAnotherClass
AnotherClassProperties 1
//Example class
public class MyClass
{
public int ID { get; set; }
public string Name { get; set; }
public string AnotherProperty { get; set; }
public AnotherClass ThisIsAnotherClass { get; set; }
}
So I'm taking each property name, writing it, a blank space, then it's value (if there is any).
Now I'm trying to implement support for lists and anything array-like for something like this:
MyClass
ArrayTest
1
2
3
If it's a class, I'll have a recursive for the function so I can display all values inside the list/array in this pattern. (it's for a webservice)
My question is, how can I find if a specific property is something list-able?
I've tried:
Type type = myObject.GetType();
PropertyInfo[] properties = type.GetProperties();
for(int i = 0; i < properties.Length; i++)
{
if(properties[i].PropertyType.IsGeneric) //Possible List/Collection/Dictionary
{
//Here is my issue
Type subType = properties[i].PropertyType.GetGenericTypeDefinition();
bool isAssignable = subType.IsAssignableFrom(typeof(ICollection<>)); //Always false
bool isSubclass = subType.IsSubclassOf(typeof(ICollection<>)); //Always false
//How can I figure if it inherits ICollection/IEnumerable so I can use it's interface to loop through it's elements?
}
else if(properties[i].PropertyType.IsArray) //Array
{
}
else if(properties[i].PropertyType.IsClass && !properties[i].PropertyType.Equals(typeof(String)))
{
//Non-string Subclasses, recursive here
}
else
{
//Value types, write the text + value
}
}
Like mentioned in the comments: use Json as a way to format objects, it will save a lot of time.
If you have reason not to do this, you can check if the type is enumerable: This also covers the Type.IsArray case.
typeof(IEnumerable).IsAssignableFrom(properties[i].PropertyType)
As an added notice of caution: maybe you do not want to enumerate String and byte[] type objects.
I'm using reflection to get all properties of a specific object and iterate over them.
I'm calling these properties props.
When those are non-List objects, this all works fine.
I need a way to know when a prop I'm iterating over is a list (I saw there is a prop.PropertyType.IsArray for instance, but not one for list).
I saw here Determine if a property is a kind of array by reflection that they suggested to use:
property.PropertyType.GetInterface(typeof(IEnumerable<>).FullName) != null
But this is not good enough for me since string implements that as well.
(Right now I'm checking that prop.Namespace contains "collection" in it, obviously looking for a better way).
After I've done that, I have a working assumption that the List will be a List of a complex object. (but could be one of several complex ojects so I don't know which)
The harder part is that I need to iterate over those objects and get their members as well, but have failed to do so.
So if I have List<TestObject> as my prop I need to have a foreach (TestObject testObj in prop) somehow (in concept).
Don't have a reasonable attempt to show here, since I haven't yet figured out a way to treat prop as a List of my complex object and iterate over it.
EDIT: I've managed to get the value with prop.GetValue(reflectedObject) but I have to cast that to List<MyComplexObject>
I think looking into this code sample can help.
public class MyClass
{
public string Name { get; set; }
public int Id { get; set; }
public List<int> Subordinates { get; set; }
}
Use MyClass:
var myClass = new MyClass
{
Id = 1,
Name = "My Name",
Subordinates = new List<int> { 2, 5, 8 }
};
var props = myClass.GetType().GetProperties();
foreach (var info in props)
{
var type = info.PropertyType;
if (type.IsGenericType && (type.GetGenericTypeDefinition() == typeof(List<>)))
{
foreach (var listitem in info.GetValue(myClass, null) as IEnumerable)
{
Console.WriteLine("Item: " + listitem.ToString());
}
continue;
}
Console.WriteLine(info.GetValue(myClass, null));
}
I think that working with the IEnumerable<> interface is actually the way forward. Do you have some sort of a base type for your objects? If yes, then you can work around the issue with string be looking for collections of objects that extend the base type. If no, then you can treat strings as a special case before you check for the IEnumerable<> interface.
I am using the following class to serialize individual objects (T) with a prefix, and then attempting to deserialize back to a list w/ the DeserializeItems() method:
public class ProtobufSerializationProvider<T> : ISerializationProvider<T>
{
readonly Type type;
readonly TypeModel model;
public ProtobufSerializationProvider()
{
this.type = typeof(T);
this.model = createModel(this.type);
}
public byte[] Serialize(T instance)
{
byte[] buffer;
using (MemoryStream strm = new MemoryStream())
{
model.SerializeWithLengthPrefix
(strm, instance, type, PrefixStyle.Base128, 1);
buffer = strm.GetBuffer();
}
return buffer;
}
// here is the problem method
public IEnumerable<T> DeserializeAll(MemoryStream stream)
{
return model.DeserializeItems<T>(stream, PrefixStyle.Base128, 1);
}
TypeModel createModel(Type type)
{
try
{
RuntimeTypeModel runtimeModel = TypeModel.Create();
this.addTypeToModel(runtimeModel, type);
this.addTypePropertiesToModel(runtimeModel, type);
return runtimeModel.Compile();
}
catch (Exception e)
{
throw e.InnerException;
}
}
void addTypePropertiesToModel(RuntimeTypeModel typeModel, Type type)
{
PropertyInfo[] properties = type.GetProperties();
for (int i = 0; i < properties.Length; i++)
{
Type innerType = properties[i].PropertyType;
if (!innerType.IsPrimitive && !(innerType == typeof(string)))
{
addTypeToModel(typeModel, properties[i].PropertyType);
}
}
}
MetaType addTypeToModel(RuntimeTypeModel typeModel, Type t)
{
var properties = t.GetProperties()
.Select(p => p.Name)
.OrderBy(name => name);
return typeModel
.Add(t, true)
.Add(properties.ToArray());
}
}
I am getting a default InvalidOperationException "Operation is not valid due to the current state of the object" when I try to enumerate the IEnumerable, whether by casting ToList(), or counting it Count(), etc. Specifically the MoveNext() method will throw the error:
enumerator.MoveNext()
Also the stream has to be open until DeserializeItems(stream) returns, and I ensured that was the case. But once the IEnumerable successfully returns I cannot use it.
Not sure if there is a problem with the serialized items or not. Also I noticed that each of my items 256 bytes, although a large portion of those bytes are just trailing nulls.
This is the class I am serializing as a test. Note that I am not using attributes since I create the model manually:
public class NestedFoo
{
public string NestedFooStr { get; set; }
}
public class Foo
{
public string Foo1 { get; set; }
public string Foo2 { get; set; }
public string Foo3 { get; set; }
public string Foo4 { get; set; }
public NestedFoo NestedFoo { get; set; }
}
Thanks.
UPDATE
After switching from MemoryStream.GetBuffer() to MemoryStream.ToArray(), the message is shortened but trying to cast the IEnumerable .ToList() or any other operation produces the same error.
However, I have noticed that after switching to .ToArray() the 'Current' property of the IEnumerable after enumeration reaches the last element in the IEnumerable before throwing the error, whereas when using .GetBuffer() the 'Current' property on the first element when the error is thrown.
I suspect the problem could be that the IEnumerable<> doesn't know when its out of elements because its reached the end of the stream? I append several items serialized with SerializeWithLengthPrefix() to a single byte[], and then there may be nulls left over trailing the array. If I read that entire array into the DeserializeItems method, does it need to know how to terminate or does it only continue if it detects a length prefix (as I would expect?).
Also one other question I had was to what extent using the SerializeWithLengthPrefix() method is equivalent to just serializing a List in one operation, using the DataFormat.Group enumeration, like:
[ProtoContract]
public class ProtoList<T>
{
[ProtoMember(1, DataFormat = DataFormat.Group)]
public List<T> List { get; set; }
public ProtoList() { }
public ProtoList(IEnumerable<T> items)
{
this.List = new List<T>(items);
}
}
Using a container such as this seems to perform very well, so I may just go with this route in cases where I want to serialize a list in one shot.
However for serializing items individually as they are added I am currently using my own prefixing function, so if I can get the Protobuf prefixing methods to work that would be good too.
The 256/trailing nulls is because you are using MemoryStream.GetBuffer(). This method is fine as long as it is used in combination with the Length, as the buffer is oversized. If you want a right-sized array, use ToArray() instead. Alternatively use ArraySegment<byte> to avoid copying any data.
It is possible this also fixes the exception (0 is not a valid value for a field-header). If not, leave a comment so I know to look in more detail. If it does fix it, please also let me know, and I'll try to make the error message (for the 0 field-header case) more helpful.
Just to confirm, ran into same problem.
Do not use tags / packet ids as 0. Solved everything.
There is already a similar question but it didn't seem to ask about the situation that the question implies.
The user asked about custom classes in a list but his list object is of type string.
I have a class Foo that has a list of Bars:
public class Foo : FooBase
{
public List<Bar> bars {get; set;}
public Foo() {}
}
public class Bar
{
public byte Id { get; set; }
public byte Status { get; set; }
public byte Type { get; set; }
public Bar(){}
}
I instantiate Foo using reflection via Activator.CreateInstance(). Now I need to populate that list of bars with Bar objects.
Foo is obtained using
Assembly.GetAssembly(FooBase).GetTypes().Where(type => type.IsSubclassOf(FooBase));
Bar is a public class in the same Assembly. I'll need to get at that type somehow. I can't seem to see what the type of the list contained in Foo is. I know it's a list though. I'm seeing the list property as List`1.
I'd need to see what type of object the list holds and handle that accordingly.
The text
List`1
is the way that generics are written under the bonnet - meaning "List with 1 generic type arg, aka List<>". If you have a PropertyInfo, you should be set; this will be the closed generic List<Bar>. Is it that you want to find the Bar given just this?
If so, this is discussed in various questions, including this one; to duplicate the key bit (I prefer to code against IList<T>, since it handles a few edge-cases such as inheriting from List<T>):
static Type GetListType(Type type) {
foreach (Type intType in type.GetInterfaces()) {
if (intType.IsGenericType
&& intType.GetGenericTypeDefinition() == typeof(IList<>)) {
return intType.GetGenericArguments()[0];
}
}
return null;
}
var prop = footype.GetProperty("bars");
// In case you want to retrieve the time of item in the list (but actually you don't need it...)
//var typeArguments = prop.PropertyType.GetGenericArguments();
//var listItemType = typeArguments[0];
var lst = Activator.CreateInstance(prop.PropertyType);
prop.SetValue(foo, lst, null);
I have a couple of objects that have a corresponding object with identical properties.
class Source1
{
int id;
string name;
DateTime date;
}
class Destination1
{
int id;
string name;
DateTime date;
}
class Source2
{
int id;
string code;
double price;
}
class Destination2
{
int id;
string code;
double price;
}
Now I want to make a method with generic types that can cast an object into his corresponding object.
public TDestination Cast<TSource, TDestination>(TSource source)
{
//TDestination destination = (TDestination) source;
return destination;
}
Your best option here is to introduce a common interface (or base class). There is no other way to cast an item into another.
public interface IItem
{
int id {get;set;}
string name {get;set;}
DateTime date {get;set;}
}
class Source1 : IItem
{
public int id {get;set;}
public string name {get;set;}
public DateTime date {get;set;}
}
class Destination1 : IItem
{
public int id {get;set;}
public string name {get;set;}
public DateTime date {get;set;}
}
You can then just cast the object into the interface and access the properties.
var item1 = (IItem)sourceItem;
var item2 = (IItem)destinationItem;
If you don't want to do that another option would be to using reflection go trough the properties in the source and create a new object of the destination type and try to map properties with shared names. This would however create a new object and is not the same at all as casting. There are libraries such as AutoMapper that can help you with this.
AutoMapper.Mapper.CreateMap<Source1, Destination1>();
var destItem = AutoMapper.Mapper.Map<Destination1 >(sourceItem);
As Magnus points out, if you want to actually cast, interfaces are your way to go. However, I'm thinking you might be actually looking at converting rather then casting.
I would consider checking Automapper as it exists for exactly that.
From the documentation:
var source = new Source<int> { Value = 10 };
var dest = mapper.Map<Source<int>, Destination<int>>(source);
dest.Value.ShouldEqual(10);
You can configure mappings between two types by either naming conventions (even custom naming conventions), custom mappers or a combination of both. The most common use case I have seen for this is mapping a datatransfer object to a model and back again.
If one object is basically the other object with some extra logic and properties the Decorator pattern might be what you are looking for. Here you basically wrap (decorate) one object with some extra stuff and the decorating object links everything through to the original object.
Boris Calens pointed out Automapper, which is great, but if you want to avoid using outside code, a home-made solution for your example problem is pretty simple:
using System.Reflection;
...
TDestination Copy<TSource, TDestination>(TSource source)
where TDestination : new()
{
TDestination dest = new TDestination();
foreach (FieldInfo srcField in typeof(TSource).GetFields())
{
foreach (FieldInfo destField in typeof(TDestination).GetFields())
{
if (destField.Name == srcField.Name && destField.FieldType == srcField.FieldType)
{
destField.SetValue(dest, srcField.GetValue(source));
}
}
}
return dest;
}
You could also easily loop through the properties of the respective types; specify binding flags to filter which fields/properties get copied; and expand the comparison for determining whether the two members are of the same type (i.e. checking whether one type is derived from another).
My answer to this question (which you may also find helpful) shows a similar example, comparing properties and fields.