I am reading the "Head First Object Oriented Design and Analysis" and I am stuck on page 254.
In the java code below, I am trying to convert the "Matches" method to a c# one.
public class InstrumentSpec {
private Map properties;
public InstrumentSpec(Map properties) {
if (properties == null) {
this.properties = new HashMap();
} else {
this.properties = new HashMap(properties);
}
}
public Object getProperty(String propertyName) {
return properties.get(propertyName);
}
public Map getProperties() {
return properties;
}
public boolean matches(InstrumentSpec otherSpec) {
for (Iterator i = otherSpec.getProperties().keySet().iterator();
i.hasNext(); ) {
String propertyName = (String)i.next();
if (!properties.get(propertyName).equals(
otherSpec.getProperty(propertyName))) {
return false;
}
}
return true;
}
}
And this is the C# code that I have so far:
public class InstrumentSpec
{
private IDictionary _properties;
public InstrumentSpec(IDictionary properties)
{
this._properties = properties == null ? new Hashtable() : new Hashtable(properties);
}
public object GetProperty(string propertyName)
{
return _properties.Contains(propertyName);
}
public IDictionary Properties
{
get { return _properties; }
set { _properties = value; }
}
public virtual bool Matches(InstrumentSpec otherSpec)
{
foreach (var prop in otherSpec.Properties)
{
if (!prop.Equals(otherSpec.Properties))
{
return false;
}
}
return true;
}
}
Anyone has got any idea how to make the Matching method work so that it checks if two objects match?
The Java code iterates over the dictionary keys and compares the respective property values. You're currently iterating over the key/value pairs and compare them to the dictionary.
I guess something like
foreach (var key in otherSpec.Properties.Keys)
{
if (!Properties[key].Equals(otherSpec.Properties[key]))
{
return false;
}
}
return true;
would be a better translation.
Look at your comparison:
if (!prop.Equals(otherSpec.Properties))
When do you expect any single "property" to equal the collection of "properties" which contains it? The Java code is making comparisons with the object's internal collection of "properties":
if (!properties.get(propertyName).equals(
otherSpec.getProperty(propertyName)))
Which basically means it's looping through a collection of "properties" and for each "property" in that collection it is comparing it with a similarly named "property" in another collection. But you don't make any reference to the object's collection here:
private IDictionary _properties;
You need to compare the values from one collection to the values in the other collection. Without doing any checking if the values actually exist in the collection (which I recommend doing), it might look something like this:
foreach (var prop in otherSpec.Properties.Keys)
{
if (!otherSpec.Properties[prop].Equals(_properties[prop]))
{
return false;
}
}
return true;
You could completely copy the algorithm, if thats what you want:
public virtual bool Matches(InstrumentSpec otherSpec)
{
foreach (var prop in otherSpec.Properties.Keys)
{
if (!Object.equals(properties[prop], otherSpec[prop]))
{
return false;
}
}
return true;
}
But i would advise, to use generics, to know, which types we are talking about
Try this:
var keysEqual= Properties.Keys.SequenceEqual(otherSpec.Properties.Keys);
var valuesEqual = Properties.Values.SequenceEqual(otherSpec.Properties.Values);
if(keysEqual && valueEqual)
{
//objects have the same properties and values
}
Related
I'm experimenting with reflection in c# for the first time (for use in a dev tool I'm building) and I was wondering if someone could help me to access the thing that I'm trying to access?
The code below is the class I'm accessing:
[Serializable]
public class MaterialVariantTarget : BaseNode, IMaterialTarget
{
public MeshRenderer m_Target;
public void ApplyValue(Material value) => m_Target.material = value;
}
The question I want to answer is what is the name of the value that ApplyValue() operate on. So in this example it would be MeshRenderer.material
I've broken the problem down into two parts. Accessing m_Target's Type (MeshRenderer) and the .material property.
The first, I've managed to access:
private Type GetTargetComponentType(Type targetNodeType)
{
foreach (var fieldInfo in targetNodeType.GetFields())
{
if (fieldInfo.Name == "m_Target")
{
var type = fieldInfo.FieldType;
return type;
}
}
return null;
}
I'm finding accessing the scond part more tricky. Is what I'm trying to do even possible and if so, how can I do it?
Many Thanks
[UPDATE]
So the consensus seems to be that I can't access the contents of the method.
I'm going to have to just resort to writing out the info I need as a string hich can then be read but its not ideal :(
Might I have more options if I were to arrange it as a property get/setter? like this:
[Serializable]
public class MaterialVariantTarget : BaseNode, IMaterialTarget
{
public MeshRenderer m_Target;
private Material m_valueProperty
{
get => m_Target.material;
set => m_Target.material = value;
}
public void ApplyValue(Material value) => m_valueProperty = value;
}
Here's two handy extensions I made to retrieve the value of a field or property of an object based on a name or a type:
public static class Extensions
{
public static object GetPropertyOrFieldByName(this object obj, string nameToSearch)
{
foreach (var field in obj.GetType().GetFields())
{
if (field.Name == nameToSearch)
{
return field.GetValue(obj);
}
}
foreach (var property in obj.GetType().GetProperties())
{
if (property.Name == nameToSearch)
{
return property.GetValue(obj);
}
}
return null;
}
public static T GetPropertyOrFieldByType<T>(this object obj) where T : Object
{
foreach (var field in obj.GetType().GetFields())
{
if (field.FieldType == typeof(T))
{
return (T)field.GetValue(obj);
}
}
foreach (var property in obj.GetType().GetProperties())
{
if (property.PropertyType == typeof(T))
{
return (T)property.GetValue(obj);
}
}
return null;
}
}
The usage you require could be implemented this way:
object target = yourMaterialVariantTarget.GetPropertyOrFieldByName("m_Target");
Material material = target.GetPropertyOrFieldByType<Material>();
material.color = Color.red;
I have this scenario:
private bool form_1_Enabled = true;
private new Dictionary<string,bool> dict = new Dictionary<string,bool>()
{
{ 'is_form_1_enabled', this.form_1_Enabled }
};
for(var i in dict)
{
if (i.Value == true)
{
i.Value = false; // this should change form_1_Enabled
}
}
so, the idea is to change the passed property.
Is something like that possible?
The only solution i've found was:
(dynamic)this.GetType().GetProperty(i.Value).GetValue(this, null) = false;
As soon as you have to copy and maintain duplicate state you should think a different solution. Keeping state in sync is expensive and error prone.
Some alternatives (in no particular order)
Use the dictionary and have other code access that directly or indirectly (by indirect I mean you could have a helper function that returns a value based on some parameter).
Seems your code uses the dictionary only to loop through the private variables and set their value. Instead of a dictionary use reflection on the instance to find all private fields instances of type boolean, with additional checks as necessary like on name or an attribute marker, and (re)set the value that way.
Example:
using System.Linq;
using System.Reflection;
public void Reset()
{
foreach (var field in this.GetType().GetFields(BindingFlags.Instance | BindingFlags.NonPublic)
.Where(x=>x.Name.EndsWith("Enabled", StringComparison.OrdinalIgnoreCase) && x.FieldType == typeof(bool)))
{
field.SetValue(this, false);
}
}
Because in c# a bool is a value type it is always copied by value. If you want to copy it by reference you could write a wrapper for the value type
class A
{
private BoolWrapper form_1_Enabled = new BoolWrapper(true);
private new Dictionary<string, BoolWrapper> dict;
public A()
{
dict = new Dictionary<string, BoolWrapper>() { { "is_form_1_enabled", form_1_Enabled }, };
foreach (var i in dict)
{
if (i.Value.Value == true)
{
i.Value.Value = false;
}
}
}
public class BoolWrapper
{
public bool Value { get; set; }
public BoolWrapper(bool value) { this.Value = value; }
}
}
I need to recursively get all DateTime properties of an object.
Currently I'm doing:
public static void GetDates(this object value)
{
var properties = value.GetType().GetProperties();
foreach (var property in properties)
{
if (property.GetType().IsClass)
{
property.SetDatesToUtc();
}
else
{
if (property.GetType() == typeof(DateTime))
{
//Do something...
}
}
}
}
However, using property.GetType().IsClass is not enough as even strings or date properties are classes.
Is there a way to get properties that are actual classes?
Would it be better if I add an interface to the classes that have DateTime properties and then check if that property implements that interface?
You are on the right track, but I think your logic is a little reversed. You should be changing date times, and running the same method on everything else:
public static void GetDates(this object value)
{
if(value == null) //if this object is null, you need to stop
{
return;
}
var properties = value.GetType().GetProperties();
foreach(PropertyInfo property in properties)
{
//if the property is a datetime, do your conversion
if(property.GetType() == typeof(DateTime))
{
//do your conversion here
}
//otherwise get the value of the property and run the same logic on it
else
{
property.GetValue(value).GetDates(); // here is your recursion
}
}
}
I added an interface to the classes that have a DateTime property. So method changes to:
public static void GetDates(this object value)
{
var properties = value.GetType().GetProperties();
foreach (var property in properties)
{
if (typeof(IHasDateProperty).IsAssignableFrom(property.PropertyType))
{
property.SetDatesToUtc();
}
else
{
if (property.GetType() == typeof(DateTime))
{
//Do something...
}
}
}
}
I have this class
public class MyViewModel {
public MyClass Thing { get; set; }
public int Id { get { return Thing.Id; } }
public string Name { get { return Thing.Name; } }
}
I noticed when I bind it to an ASP.NET GridView, it automatically omits Thing, and for a good reason (ie. because otherwise it will only show the meaningless "MyNamespace.MyClass" in all rows)
I am trying to do a similar thing in this method.
public static string ConvertToCsv<T>(IEnumerable<T> items)
{
foreach (T item in items)
{
if(item is not a native/.NET class) // <-- How do you do this?
continue;
else // If it is a string/int/bool/DateTime or something meaningful
{
...
}
}
}
Not sure about performance, but you could use somthing along the lines of
if(item.GetType().Namespace.StartsWith("System"))
{
// do stuff
}
Or filter before looping
public static string ConvertToCsv<T>(IEnumerable<T> items)
{
foreach (T item in items.Where(i => i.GetType().Namespace.StartsWith("System")))
{
}
}
Edit: after a quick test the method above has some flaws, If your object is nullable (MyViewModel?) it will be picked up in this check (System.Nullable<MyViewModel>).
So perhaps you could use:
public static string ConvertToCsv<T>(IEnumerable<T> items)
{
foreach (T item in items.Where(i => i.GetType().Module.ScopeName.Equals("CommonLanguageRuntimeLibrary")))
{
}
}
Another edit:
There seems to be some issue with the last method also, But this one below is by far the fastest and most reliable, We just create a list of the System.Objects from the Assembly, and check if your item object is in that list.
private List<Type> _systemTypes;
public List<Type> SystemTypes
{
get
{
if (_systemTypes == null)
{
_systemTypes = Assembly.GetExecutingAssembly().GetType().Module.Assembly.GetExportedTypes().ToList();
}
return _systemTypes;
}
}
public static string ConvertToCsv<T>(IEnumerable<T> items)
{
foreach (T item in items.Where(i => SystemTypes.Contains(i.GetType())))
{
// is system type
}
}
You would have to look it up in a predefined hash table or dictionary ... say by enumerating all the assemblies that are part of the .NET Framework SDK and storing the fully qualified name in a dictionary.
I know this is old but I think you are looking for Method.
Type type = result.GetType();
PropertyInfo[] properties = type.GetProperties();
foreach (PropertyInfo property in properties)
{
string prs = property.GetMethod.ToString();
if(!prs.StartsWith("System"))
{
//IS CLass
} else {
Console.WriteLine(property.Name + ":::" + property.Value);
}
}
I have a method where I am passing in two object, which have the same property names, and I'm using Reflection to get the values from one object and set the values on the other. My problem is when I come across a property that is a collection, the original is EntityCollection and the one getting set is ObservableCollection, and I'm obviously going to throw a casting error when I try to set the value.
So how would I go about this? I thought one way would be to get the instance of the EntityCollection property and in a loop use Activator.CreateInstance() to add a new item to the ObservableCollection. But I come across another question of how to get the original instance.
I would be greatly appreciative for some insight into this dilemma. I'll post the code for the method that I'm working with. It currently doesn't work, mainly I posted it just for a reference. So please don't point out the obvious. Thanks in advance.
protected void SetValues(Object thisObject, Object entity)
{
PropertyInfo[] properties = entity.GetType().GetProperties();
foreach (PropertyInfo property in properties)
{
var value = property.GetValue(entity, null);
var thisObjectsProperty = thisObject.GetType().GetProperty(property.Name);
if (thisObjectsProperty != null && value != null)
{
if (thisObjectsProperty.PropertyType.GetInterface("ICollection", true) != null && thisObjectsProperty.PropertyType.GetGenericArguments().Count() > 0)
{
Type genericType = thisObjectsProperty.PropertyType.GetGenericArguments()[0];
Type entityCollectionType = property.PropertyType;
Type thisCollectionType = thisObjectsProperty.PropertyType;
IList entityCollection = (IList)Activator.CreateInstance(entityCollectionType.MakeGenericType(genericType));
IList observableCollection = (IList)Activator.CreateInstance(thisCollectionType.MakeGenericType(genericType));
foreach (var item in entityCollection)
{
String typeString = String.Concat("RulesGenerator.DependencyObjects.", genericType.Name);
Type newItemType = Type.GetType(typeString, false, true);
if (newItemType != null)
{
var newItem = Activator.CreateInstance(newItemType);
SetValues(newItem, item);
observableCollection.Add(newItem);
}
}
}
else
thisObjectsProperty.SetValue(thisObject, value, null);
}
}
}
Implement the ICloneable interface to provide a deep copy. You should be able to find some examples, but here is a starting point:
http://msdn.microsoft.com/en-us/library/system.icloneable%28v=VS.71%29.aspx
http://en.csharp-online.net/ICloneable
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
public class testMain : ICloneable
{
private string m_TestProp;
private List<Record> m_Items = new List<Record>();
public string TestProp
{
get { return m_TestProp; }
set { m_TestProp = value; }
}
public List<Record> Items
{
get { return m_Items; }
}
public object Clone()
{
testMain cpy =(testMain) this.MemberwiseClone();
foreach (Record rec in this.Items)
{
Record recCpy = (Record)rec.Clone();
cpy.Items.Add(recCpy);
}
return cpy;
}
}
public class Record : ICloneable
{
private string m_TestProp;
public string TestProp
{
get { return m_TestProp; }
set { m_TestProp = value; }
}
public object Clone()
{
return this.MemberwiseClone();
}
}