In this question
Transactions for C# objects?
user nicolas2008 posted the code which is able to do rollback to changed object. I paste the code below.
I would like to ask is that code safe to use or do you see some dangers in that? Also how it compares to Memento pattern?
public sealed class ObjectTransaction : IDisposable
{
bool m_isDisposed;
Dictionary<object, object> sourceObjRefHolder;
object m_backup;
object m_original;
public ObjectTransaction(object obj)
{
sourceObjRefHolder = new Dictionary<object, object>();
m_backup = processRecursive(obj, sourceObjRefHolder, new CreateNewInstanceResolver());
m_original = obj;
}
public void Dispose()
{
Rollback();
}
public void Rollback()
{
if (m_isDisposed)
return;
var processRefHolder = new Dictionary<object, object>();
var targetObjRefHolder = sourceObjRefHolder.ToDictionary(x => x.Value, x => x.Key);
var originalRefResolver = new DictionaryRefResolver(targetObjRefHolder);
processRecursive(m_backup, processRefHolder, originalRefResolver);
dispose();
}
public void Commit()
{
if (m_isDisposed)
return;
//do nothing
dispose();
}
void dispose()
{
sourceObjRefHolder = null;
m_backup = null;
m_original = null;
m_isDisposed = true;
}
object processRecursive(object objSource, Dictionary<object, object> processRefHolder, ITargetObjectResolver targetResolver)
{
if (objSource == null) return null;
if (objSource.GetType() == typeof(string) || objSource.GetType().IsClass == false) return objSource;
if (processRefHolder.ContainsKey(objSource)) return processRefHolder[objSource];
Type type = objSource.GetType();
object objTarget = targetResolver.Resolve(objSource);
processRefHolder.Add(objSource, objTarget);
if (type.IsArray)
{
Array objSourceArray = (Array)objSource;
Array objTargetArray = (Array)objTarget;
for (int i = 0; i < objSourceArray.Length; ++i)
{
object arrayItemTarget = processRecursive(objSourceArray.GetValue(i), processRefHolder, targetResolver);
objTargetArray.SetValue(arrayItemTarget, i);
}
}
else
{
IEnumerable<FieldInfo> fieldsInfo = FieldInfoEnumerable.Create(type);
foreach (FieldInfo f in fieldsInfo)
{
if (f.FieldType == typeof(ObjectTransaction)) continue;
object objSourceField = f.GetValue(objSource);
object objTargetField = processRecursive(objSourceField, processRefHolder, targetResolver);
f.SetValue(objTarget, objTargetField);
}
}
return objTarget;
}
interface ITargetObjectResolver
{
object Resolve(object objSource);
}
class CreateNewInstanceResolver : ITargetObjectResolver
{
public object Resolve(object sourceObj)
{
object newObject = null;
if (sourceObj.GetType().IsArray)
{
var length = ((Array)sourceObj).Length;
newObject = Activator.CreateInstance(sourceObj.GetType(), length);
}
else
{
//no constructor calling, so no side effects during instantiation
newObject = System.Runtime.Serialization.FormatterServices.GetUninitializedObject(sourceObj.GetType());
//newObject = Activator.CreateInstance(sourceObj.GetType());
}
return newObject;
}
}
class DictionaryRefResolver : ITargetObjectResolver
{
readonly Dictionary<object, object> m_refHolder;
public DictionaryRefResolver(Dictionary<object, object> refHolder)
{
m_refHolder = refHolder;
}
public object Resolve(object sourceObj)
{
if (!m_refHolder.ContainsKey(sourceObj))
throw new Exception("Unknown object reference");
return m_refHolder[sourceObj];
}
}
}
class FieldInfoEnumerable
{
public static IEnumerable<FieldInfo> Create(Type type)
{
while (type != null)
{
var fields = type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
foreach (FieldInfo fi in fields)
{
yield return fi;
}
type = type.BaseType;
}
}
}
My code creates realy deep copy of object for support rollback.
It is not fires any events.
It is not calls any methods, constructors or property accessors.
It can be dangerous if object tree contains references to:
unmanaged resources (they can not be copied correctly, I think),
shared objects (because they also will be copied/rolled back).
You must analyze this risks.
I suggest you to add type filter (in the beginning of processRecursive method) or filter by other field metadata (in foreach loop) to skip perform deep copying such objects. For example:
object processRecursive(object objSource, Dictionary<object, object> processRefHolder, ITargetObjectResolver targetResolver)
{
if (objSource == null) return null;
//perform filter by type:
if (typeof(System.Data.IDbConnection).IsAssignableFrom(objSource)) return objSource;
//...
foreach (FieldInfo f in fieldsInfo)
{
if (f.FieldType == typeof(ObjectTransaction)) continue;
object objSourceField = f.GetValue(objSource);
object objTargetField = Attribute.IsDefined(f,typeof(MyAttributeForSkipTransaction)) //perform filter by field attribute
? objSourceField
: processRecursive(objSourceField, processRefHolder, targetResolver);
f.SetValue(objTarget, objTargetField);
}
//...
}
In memento pattern ObjectTransaction plays role of Memento object: stores state of object.
SaveToMemento: create new ObjectTransaction(thisObj).
RestoreFromMemento: rollback created ObjectTransaction.
Related
I'm creating a function to loop over an object and its childs.
But I'm having some issue when i try to map from the original object to the new object, here is the code:
public static bool MatchObjectField<T>(this T obj, string objectRoute, string value)
{
try
{
var objectroutelist = objectRoute.Split('.');
var objroute = objectroutelist.First();
var childproperty = typeof(T).GetProperty(objroute);
if (objectroutelist.Count() == 1)
{
if (childproperty.GetValue(obj).ToString().Trim() == value)
{
return true;
}
return false;
}
else
{
var instance = Activator.CreateInstance(childproperty.PropertyType);
//childproperty.SetValue(obj, instance, null);
//childproperty.SetValue(instance, obj, null);
instance.MapValues(childproperty);
instance.MatchObjectField(string.Join(".", objectroutelist.Skip(1)), value);
}
}
catch (Exception e)
{
return false;
}
return false;
}
And here the class where I do the map and contains the issue.
public static void MapValues<T>(this T destination, PropertyInfo orgproperty)
{
var orgvalues = orgproperty.GetPropertiesWithValues();
var instance = Activator.CreateInstance(typeof(T));
foreach (var property in (typeof(T)).GetProperties())
{
try
{
var value = orgvalues.FirstOrDefault(a => a.Key == property.Name);
property.SetValue(instance, value);
}
catch (Exception)
{
property.SetValue(instance, null);
}
}
destination = (T)(object)instance;
}
The idea of the function is call with objectName.MatchObjectField("parent.child.child.child","MyName")
When I try to compare a field in the parent like objectName.MatchObjectField("Country","Ireland") it works perfectly
But when I try to create the child structure doing a call like objectName.MatchObjectField("Country.Address.City","Dublin") it breaks when I try to map to the new object.
What I noticed is that the property destination into the method MapValues<T> is mapped as Country.Address with all the properties as null which is correct.
But (typeof(T)).GetProperties() doesn't return anything.
Also i noticed that Activator.CreateInstance(typeof(T)) retunrs type {object} instead of return Country.Address
that may be the reason why is not working.
Any idea how to fix this?
EDIT: add get properties with values-> it retuns a Dictionary<string, object>
public static Dictionary<string, object> GetPropertiesWithValues(this Object obj, bool includeValueTypes = true)
{
return InternalGetProperties(obj, false, includeValueTypes);
}
private static Dictionary<string, object> InternalGetProperties(Object obj, bool withDefaultValue, bool includeValueTypes = true)
{
Dictionary<string, object> d = new Dictionary<string, object>();
var res = GetPropertiesR(obj, d, "", withDefaultValue, includeValueTypes);
return res;
}
private static Dictionary<string, object> GetPropertiesR(Object obj, Dictionary<string, object> d, string parent, bool searchingForDefaults, bool includeValueTypes)
{
if (obj == null)
return d;
var pis = #obj.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public);
if (pis == null || pis.Length == 0)
throw new InvalidOperationException("This object doens't have inner properties");
Func<string, string> formatProperty = (property) => string.Concat(parent, parent == "" ? "" : ".", property);
foreach (var pi in pis)
{
object data = pi.GetValue(#obj, null);
// check if is value type
if (pi.PropertyType.IsValueType)
{
// data is never null
if (!includeValueTypes)
continue;
Type nullableType = Nullable.GetUnderlyingType(pi.PropertyType);
object defaultValue = nullableType != null ? GetDefault(nullableType) : GetDefault(data.GetType());
if (!searchingForDefaults)
{
// check default values.
if (data != null && data.ToString() != defaultValue.ToString())
d.Add(formatProperty(pi.Name), data);
}
else
{
// check default values
if ((nullableType != null && data == null) || data.ToString() == defaultValue.ToString())
d.Add(formatProperty(pi.Name), data);
}
}
else
{
//
// reference types
if (!searchingForDefaults)
{
if (data == default(object))
continue;
if (IsThisPropertyPartofSystemNamespace(pi))
{
// transform list into a string with values.
IEnumerable enumeration = data as IList;
if (enumeration != null)
{
StringBuilder sb = new StringBuilder();
foreach (var i in enumeration)
sb.Append(string.Concat(i.ToString(), ", "));
if (sb.Length >= 2)
sb.Remove(sb.Length - 2, 2);
data = sb.ToString();
}
d.Add(formatProperty(pi.Name), data);
}
else
{
//
// user complex type defined
string ctxParent = string.Concat(parent, parent == "" ? "" : ".", pi.Name);
GetPropertiesR(data, d, ctxParent, searchingForDefaults, includeValueTypes);
}
}
else
{
if (data != default(object))
continue;
d.Add(formatProperty(pi.Name), data);
}
}
}
return d;
}
private static bool IsThisPropertyPartofSystemNamespace(PropertyInfo pi)
{
var systemNames = new HashSet<string>
{
"mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken={TOKENKEY}",
"System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken={TOKENKEY}"
};
var isSystemType = systemNames.Contains(pi.PropertyType.Assembly.FullName);
return isSystemType;
}
private static object GetDefault(Type type)
{
if (type.IsValueType)
{
return Activator.CreateInstance(type);
}
return null;
}
TL;DR: Take a step back. Describe the responsibility of each of the methods separately, and write tests for them that way. Start with the "lowest level" method and work your way up. That will make it a lot easier to see what's going wrong.
There are multiple problems here. First look at these two lines of code:
var instance = Activator.CreateInstance(childproperty.PropertyType);
instance.MapValues(childproperty);
The return type of Activator.CreateInstance is object, so this is effectively:
var instance = Activator.CreateInstance(childproperty.PropertyType);
instance.MapValues<object>(childproperty);
That's not what you want - you want to use childproperty.PropertyType as the type argument to MapValues. You don't know that at compile-time, so it's not a good fit as a type parameter.
But beyond that, your MapValues method has a bigger problem: it basically ignores its parameter. The only time it uses destination is in the last line, when it assigns a new value to it:
destination = (T)(object)instance;
That parameter is a value parameter, so assigning it at the end of the method is pointless.
You should decide what the purpose of MapValues is:
Is it to create an instance and populate it, then return it?
Is it to accept an existing instance and populate that?
Either is simple enough to implement, but at the moment you're half way between the two. Also note that you're only passing in a single PropertyInfo - consider how you'd expect to assign multiple properties with that.
Finally, there's the matter of where the values come from. You're currently calling GetPropertiesWithValues on the PropertyInfo - that's not going to do what I think you expect it to. You need to provide the source object itself, otherwise there's nowhere to get values from.
I found what i need to do in this post: Getting Nested Object Property Value Using Reflection
getting the nested object is an easy way of what i was pretending to do.
public static object GetPropertyValue(object src, string propName)
{
if (src == null) throw new ArgumentException("Value cannot be null.", "src");
if (propName == null) throw new ArgumentException("Value cannot be null.", "propName");
if(propName.Contains("."))//complex type nested
{
var temp = propName.Split(new char[] { '.' }, 2);
return GetPropertyValue(GetPropertyValue(src, temp[0]), temp[1]);
}
else
{
var prop = src.GetType().GetProperty(propName);
return prop != null ? prop.GetValue(src, null) : null;
}
}
public static bool MatchObjectField<T>(this T obj, string objectRoute, string value)
{
try
{
var propvalue = GetPropertyValue(obj, objectRoute);
return ( propvalue.ToString().Trim() == value.Trim());
}
catch (Exception) {
throw;
}
}
I have a need to update object A's property if null with that from object B's equivalent property if that is not null. I wanted code I can use for various objects.
I had a version working until one of the objects contained a propert of type List, which is where I have a blank in the code below. My main question is how can I best implement this part of the code. Secondly is there a better way of doing this whole thing and thirdly I know its never going to be rapid but any suggestions to speed it up would be appreciated.
Thanks in advance.
public T MergeWith<T, U>(T primarySource, U secondarySource) where U : class, T
{
Type primaryType = typeof(T);
Type secondaryType = typeof(U);
foreach (PropertyInfo primaryInfo in primaryType.GetProperties())
{
if (primaryInfo.CanWrite)
{
object currentPrimary = primaryInfo.GetValue(primarySource, null);
PropertyInfo secondaryInfo = secondaryType.GetProperty(primaryInfo.Name);
object currentSecondary = secondaryInfo.GetValue(secondarySource, null);
if (currentPrimary == null && currentSecondary != null)
{
primaryInfo.SetValue(primarySource, currentSecondary, null);
}
else if ((currentPrimary != null && currentSecondary != null) && isChildClass(primaryInfo))
{
if (isCollection(currentPrimary))
{
// here
}
else
{
MethodInfo method = typeof(NavigationModel).GetMethod("MergeWith");
MethodInfo generic = method.MakeGenericMethod(primaryInfo.PropertyType, primaryInfo.PropertyType);
object returnChild = generic.Invoke(this, new object[2] { currentPrimary, currentSecondary });
}
}
}
}
return primarySource;
}
private bool isCollection(object property)
{
return typeof(ICollection).IsAssignableFrom(property.GetType())
|| typeof(ICollection<>).IsAssignableFrom(property.GetType());
}
private bool isChildClass(PropertyInfo propertyInfo)
{
return (propertyInfo.PropertyType.IsClass && !propertyInfo.PropertyType.IsValueType &&
!propertyInfo.PropertyType.IsPrimitive && propertyInfo.PropertyType.FullName != "System.String");
}
I have created the below extension method for use in my latest project and it works fine, collections and all. It is a pretty much a simpler version of what you are doing in your method. With mine both classes have to be the same type. What problem do you encounter with collections?
public static class ExtensionMethods
{
public static TEntity CopyTo<TEntity>(this TEntity OriginalEntity, TEntity NewEntity)
{
PropertyInfo[] oProperties = OriginalEntity.GetType().GetProperties();
foreach (PropertyInfo CurrentProperty in oProperties.Where(p => p.CanWrite))
{
if (CurrentProperty.GetValue(NewEntity, null) != null)
{
CurrentProperty.SetValue(OriginalEntity, CurrentProperty.GetValue(NewEntity, null), null);
}
}
return OriginalEntity;
}
}
Hi I modified Ben Robinsons solution in order to not overwrite Collections or list, instead, it adds the elements of one object to the other one where the merging is happening:
public static class ExtensionMethods
{
public static TEntity CopyTo<TEntity>(this TEntity OriginalEntity, TEntity EntityToMergeOn)
{
PropertyInfo[] oProperties = OriginalEntity.GetType().GetProperties();
foreach (PropertyInfo CurrentProperty in oProperties.Where(p => p.CanWrite))
{
var originalValue = CurrentProperty.GetValue(EntityToMergeOn);
if (originalValue != null)
{
IListLogic<TEntity>(OriginalEntity, CurrentProperty, originalValue);
}
else
{
var value = CurrentProperty.GetValue(OriginalEntity, null);
CurrentProperty.SetValue(EntityToMergeOn, value, null);
}
}
return OriginalEntity;
}
private static void IListLogic<TEntity>(TEntity OriginalEntity, PropertyInfo CurrentProperty, object originalValue)
{
if (originalValue is IList)
{
var tempList = (originalValue as IList);
var existingList = CurrentProperty.GetValue(OriginalEntity) as IList;
foreach (var item in tempList)
{
existingList.Add(item);
}
}
}
}
I would like to dynamically get and set an objects properties as follows:
public class Person
{
public string Name {get; set; }
}
public class Testing
{
public void Run()
{
var p = new Person();
SetValue(p, "Name", "Henry");
var name = GetValue(p, "Name");
}
}
Please could I get help creating the GetValue and SetValue methods using dynamic method (or expression trees)?
I am intending to save compiled expressions in a dictionary, to speed up future get/set calls.
Do you really want to use expression trees? for this simple scenario I would try to compile directly into a DynamicMethod using Reflection.Emit API's by getting an IL Generator. But .. for expression trees, i wrote a helper for you:
public class PropertyManager : DynamicObject
{
private static Dictionary<Type, Dictionary<string, GetterAndSetter>> _compiledProperties = new Dictionary<Type, Dictionary<string, GetterAndSetter>>();
private static Object _compiledPropertiesLockObject = new object();
private class GetterAndSetter
{
public Action<object, Object> Setter { get; set; }
public Func<Object, Object> Getter { get; set; }
}
private Object _object;
private Type _objectType;
private PropertyManager(Object o)
{
_object = o;
_objectType = o.GetType();
}
public static dynamic Wrap(Object o)
{
if (o == null)
return null; // null reference will be thrown
var type = o.GetType();
EnsurePropertySettersAndGettersForType(type);
return new PropertyManager(o) as dynamic;
}
private static void EnsurePropertySettersAndGettersForType(Type type)
{
if (false == _compiledProperties.ContainsKey(type))
lock (_compiledPropertiesLockObject)
if (false == _compiledProperties.ContainsKey(type))
{
Dictionary<string, GetterAndSetter> _getterAndSetters;
_compiledProperties[type] = _getterAndSetters = new Dictionary<string, GetterAndSetter>();
var properties = type.GetProperties(System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic);
foreach (var property in properties)
{
var getterAndSetter = new GetterAndSetter();
_getterAndSetters[property.Name] = getterAndSetter;
// burn getter and setter
if (property.CanRead)
{
// burn getter
var param = Expression.Parameter(typeof(object), "param");
Expression propExpression = Expression.Convert(Expression.Property(Expression.Convert(param, type), property), typeof(object));
var lambda = Expression.Lambda(propExpression, new[] { param });
var compiled = lambda.Compile() as Func<object, object>;
getterAndSetter.Getter = compiled;
}
if (property.CanWrite)
{
var thisParam = Expression.Parameter(typeof(Object), "this");
var theValue = Expression.Parameter(typeof(Object), "value");
var isValueType = property.PropertyType.IsClass == false && property.PropertyType.IsInterface == false;
Expression valueExpression;
if (isValueType)
valueExpression = Expression.Unbox(theValue, property.PropertyType);
else
valueExpression = Expression.Convert(theValue, property.PropertyType);
var thisExpression = Expression.Property (Expression.Convert(thisParam, type), property);
Expression body = Expression.Assign(thisExpression, valueExpression);
var block = Expression.Block(new[]
{
body,
Expression.Empty ()
});
var lambda = Expression.Lambda(block, thisParam, theValue);
getterAndSetter.Setter = lambda.Compile() as Action<Object, Object>;
}
}
}
}
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
var memberName = binder.Name;
result = null;
Dictionary<string, GetterAndSetter> dict;
GetterAndSetter getterAndSetter;
if (false == _compiledProperties.TryGetValue(_objectType, out dict)
|| false == dict.TryGetValue(memberName, out getterAndSetter))
{
return false;
}
if (getterAndSetter.Getter == null)
throw new NotSupportedException("The property has no getter!");
result = getterAndSetter.Getter(_object);
return true;
}
public override bool TrySetMember(SetMemberBinder binder, object value)
{
var memberName = binder.Name;
Dictionary<string, GetterAndSetter> dict;
GetterAndSetter getterAndSetter;
if (false == _compiledProperties.TryGetValue(_objectType, out dict)
|| false == dict.TryGetValue(memberName, out getterAndSetter))
{
return false;
}
if (getterAndSetter.Setter == null)
throw new NotSupportedException("The property has no getter!");
getterAndSetter.Setter(_object, value);
return true;
}
}
And this is how you can use it:
Person p = new Person();
p.Name = "mama";
var wrapped = PropertyManager.Wrap(p);
var val = wrapped.Name; // here we are using our compiled method ...
It is very obvious that you can extract my compilation logic to use strings instead of letting DLR giving the property name for you :)
I try to create an object model for the following problem.
I need a folder object (comparable to directory folders). Each folder can contain additional sub folders and in addition parameter objects (comparable to files). In addition, each parameter needs to know in which folder it resides. This is easy so far. So I implemented the following working solution.
I have a base object, that can either be inherited to a folder or a parameter:
[Serializable()]
public class Entry
{
public Func<string> GetPath;
public string Path
{
get
{
if (GetPath == null) return string.Empty;
return GetPath.Invoke();
}
}
}
Now I created a FolderEntry, that inherits from Entry and supports adding new sub entries by implementing IList<>.
[Serializable()]
class FolderEntry : Entry, IList<Entry>
{
private readonly List<Entry> _entries;
public FolderEntry()
{
_entries = new List<Entry>();
}
public string FolderName { get; set; }
private void SetPathDelegate(Entry entry)
{
if (entry.GetPath != null) throw new ArgumentException("entry already assigned");
entry.GetPath = () =>
{
if (GetPath == null || string.IsNullOrEmpty(GetPath.Invoke())) return FolderName;
return GetPath.Invoke() + "|" + FolderName;
};
}
public void Add(Entry item)
{
SetPathDelegate(item);
_entries.Add(item);
}
[...]
}
To support Undo/Redo functionality, I made all classes serializable by adding the Serializable-Attribute.
This serialization is working so far using the following test:
var folderA = new FolderEntry();
var folderB = new FolderEntry();
folderA.Add(folderB);
var serializer = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
var memStream = new System.IO.MemoryStream();
serializer.Serialize(memStream, folderA);
Now here’s my problem. There is in addition the need that each parameter knows its index inside the hosting list. I changed my Entry-object to have a property Index and a delegate GetIndex in the same manner as Path and GetPath before:
[Serializable()]
public class Entry
{
public Func<string> GetPath;
public string Path
{
get
{
if (GetPath == null) return string.Empty;
return GetPath.Invoke();
}
}
public Func<int> GetIndex;
public int Index
{
get
{
if (GetIndex == null) return -1;
return GetIndex.Invoke();
}
}
}
Inside the SetPathDelegate of the Folder-object I assigned the new delegate
private void SetPathDelegate(Entry entry)
{
if (entry.GetPath != null) throw new ArgumentException("entry already assigned");
if (entry.GetIndex != null) throw new ArgumentException("entry already assigned");
entry.GetPath = () =>
{
if (GetPath == null || string.IsNullOrEmpty(GetPath.Invoke())) return FolderName;
return GetPath.Invoke() + "|" + FolderName;
};
entry.GetIndex = () =>
{
return _entries.IndexOf(entry);
};
}
If I try to serialize this, I get an expection that my „FolderEntry+<>c__DisplayClass2“ in Assembly… is not marked as serializable. I can’t see an obvious difference between GetPath and GetIndex. To narrow it down, I replaced content of the created GetIndex delegate in SetPathDelegate from
entry.GetIndex = () =>
{
return _entries.IndexOf(entry);
};
To
entry.GetIndex = () =>
{
return -1;
};
To my astonishment this is serializable again. Why doesn‘t cause my GetPath delegate any problems regarding the serialization but my GetIndex delegate does?
The problem is the anonymous function that you assign to GetIndex. At runtime, a new type is created, which is not marked as serializable.
According to this post, you should set a SurrogateSelector for the formatter (with some caveats, read the article in detail):
formatter.SurrogateSelector = new UnattributedTypeSurrogateSelector();
I'me pasting here the classes from the article, for future reference and in order to make the answer thorough.
public class UnattributedTypeSurrogate : ISerializationSurrogate
{
private const BindingFlags publicOrNonPublicInstanceFields =
BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public;
public void GetObjectData(object obj,
SerializationInfo info, StreamingContext context)
{
var type = obj.GetType();
foreach (var field in type.GetFields(publicOrNonPublicInstanceFields))
{
var fieldValue = field.GetValue(obj);
var fieldValueIsNull = fieldValue != null;
if (fieldValueIsNull)
{
var fieldValueRuntimeType = fieldValue.GetType();
info.AddValue(field.Name + "RuntimeType",
fieldValueRuntimeType.AssemblyQualifiedName);
}
info.AddValue(field.Name + "ValueIsNull", fieldValueIsNull);
info.AddValue(field.Name, fieldValue);
}
}
public object SetObjectData(object obj,
SerializationInfo info, StreamingContext context, ISurrogateSelector selector)
{
var type = obj.GetType();
foreach (var field in type.GetFields(publicOrNonPublicInstanceFields))
{
var fieldValueIsSerializable = info.GetBoolean(field.Name + "ValueIsNull");
if (fieldValueIsSerializable)
{
var fieldValueRuntimeType = info.GetString(field.Name + "RuntimeType");
field.SetValue(obj,
info.GetValue(field.Name, Type.GetType(fieldValueRuntimeType)));
}
}
return obj;
}
}
public class UnattributedTypeSurrogateSelector : ISurrogateSelector
{
private readonly SurrogateSelector innerSelector = new SurrogateSelector();
private readonly Type iFormatter = typeof(IFormatter);
public void ChainSelector(ISurrogateSelector selector)
{
innerSelector.ChainSelector(selector);
}
public ISerializationSurrogate GetSurrogate(
Type type, StreamingContext context, out ISurrogateSelector selector)
{
if (!type.IsSerializable)
{
selector = this;
return new UnattributedTypeSurrogate();
}
return innerSelector.GetSurrogate(type, context, out selector);
}
public ISurrogateSelector GetNextSelector()
{
return innerSelector.GetNextSelector();
}
}
I have a class, that has several elements of normal types, like int, String, etc.
It also has several elements that are various lists of other classes, that could be empty or have 1 to many items.
I have a function that I call with a generic type of the parent class, and I want to analyze data that could be in the sub elements, without knowing the types.
I am getting the parent members with the following code:
var getProperty = System.Runtime.CompilerServices.
CallSite<Func<System.Runtime.CompilerServices.CallSite,
object, object>>
.Create(Microsoft.CSharp.RuntimeBinder.
Binder.GetMember(0, property.Name, thisObject.GetType(), new[]
{
Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create(0, null)
}));
var thisValue = getProperty.Target(getProperty, thisObject);
I get the value into the var thisValue. At this point if I determine the underlying type of thisValue is a type of list, how can I grab the type of the list contents?
Here is the actual function....I can't seem to get it formatted nicely.
public static bool ObjectIsLike<T>(this T thisObject, T compareObject, params object[] argumentsToExclude)
{
for (int counter = 0; counter < argumentsToExclude.Length - 1; counter++)
{
argumentsToExclude[counter] = argumentsToExclude[counter].ToString().ToUpper();
}
bool objectIsLike = true;
foreach (var property in thisObject.GetType().GetProperties())
{
string fieldName = property.Name;
if (!argumentsToExclude.Contains(fieldName.ToUpper()))
{
try
{
var getProperty = System.Runtime.CompilerServices.CallSite<Func<System.Runtime.CompilerServices.CallSite, object, object>>.Create(Microsoft.CSharp.RuntimeBinder.Binder.GetMember(0, property.Name, thisObject.GetType(), new[] { Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create(0, null) }));
var thisValue = getProperty.Target(getProperty, thisObject);
getProperty = System.Runtime.CompilerServices.CallSite<Func<System.Runtime.CompilerServices.CallSite, object, object>>.Create(Microsoft.CSharp.RuntimeBinder.Binder.GetMember(0, property.Name, compareObject.GetType(), new[] { Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create(0, null) }));
var compareValue = getProperty.Target(getProperty, compareObject);
if (!(compareValue == null && thisValue == null))
{
if (compareValue == null || thisValue == null)
objectIsLike = false;
else
if (compareValue.GetType().FullName.Contains("List"))
{
//Ignore Lists
}
else
if (!compareValue.Equals(thisValue))
{
objectIsLike = false;
}
}
}
catch
{
objectIsLike = false;
}
}
}
return objectIsLike;
}
would GetType() work for you?
class Program
{
static void Main(string[] args)
{
MyClass1 c1 = new MyClass1();
foreach (var s in c1.pp)
{
Console.WriteLine(s.GetType());
}
Console.Read();
}
}
public class MyClass1
{
public MyClass2 p;
public List<object> pp;
public MyClass1()
{
p = new MyClass2();
pp = new List<object>();
pp.Add(new MyClass2());
pp.Add(new MyClass3());
pp.Add(new MyClass4());
}
}
public class MyClass2
{
public List<object> ppp;
public MyClass2()
{
ppp = new List<object>();
ppp.Add(new MyClass3());
ppp.Add(new MyClass4());
}
}
public class MyClass3
{
public int v;
}
public class MyClass4
{
public int v;
}