I've got an issue when using reflection.
I have a class ETSetting that contains two objects: A current value and a 'default' value. Whenever the method SetToDefault is called, I want to set the current value to the default value.
Here is my function:
public void SetToDefault() {
foreach (FieldInfo fi in Value.GetType().GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance)) {
fi.SetValue(Value, fi.GetValue(defaultVal));
}
}
However, when I call that... All sorts of issues occur. I can't say for sure but it's as if I'm reading parts of the memory that I shouldn't be (for example, when Value and defaultVal are both strings, I get all sorts of bad characters like \t, \n, and chinese characters).
Evidently I'm doing something I shouldn't... But what?
Thanks.
Edit: Here is the ETSetting class in total:
public sealed class ETSetting {
public object Value;
private object defaultVal;
public ETSetting(object defaultVal) {
Value = this.defaultVal = defaultVal;
}
public void SetToDefault() {
foreach (FieldInfo fi in Value.GetType().GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance)) {
fi.SetValue(Value, fi.GetValue(defaultVal));
}
}
}
What you want is a copy of an object that represents the default value. You can use serialization for this, or have your objects support ICloneable, for example:
public sealed class ETSetting<T> where T : ICloneable {
public T Value;
private T defaultVal;
public ETSetting(T defaultVal) {
this.defaultVal = defaultVal;
SetToDefault();
}
public void SetToDefault() {
Value = (T)defaultVal.Clone();
}
}
Related
I have a class that contains multiple string fields. Whenever an object of this class is instantiated, I'd like those fields to be automatically assigned with the same specific default value (something like "Undefined"). The reason is:
If I have to serialize the object before all fields are populated with real data, I want those fields to display as this default value rather than being null or string.Empty.
String fields may be added/removed from this class as the project progresses. I'd like to not have to touch the constructor every time that occurs.
Is there any way to do this other than explicitly assigning the default value to each of the string fields one by one in the class constructor?
In C# 6.0 and above, you can use Auto-Property Initializer:
https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-6#auto-property-initializers
Basically:
public string Property { get; set; } = "UNDEFINED";
You would have to use reflection. Something like this
Type type = obj.GetType();
PropertyInfo[] properties = type.GetProperties();
foreach (PropertyInfo property in properties)
{
if (property.PropertyType == typeof(string)) property.setValue(obj, "UNDEFINED");
}
First of all: I don't see how it could be best practice to do what you want.
If you want something like this to show up in your code:
public string Property { get; set; } = "UNDEFINED";
You should probably look into creating custom snippets that simply write exactly that. e.g. https://msdn.microsoft.com/en-us/library/ms165394.aspx
If you don't want that, you could use reflection to find all fields (e.g. strings) in the constructor and set them.
C# Reflection - Get field values from a simple class
FieldInfo[] fields = data.GetType().GetFields(BindingFlags.Public |
BindingFlags.NonPublic |
BindingFlags.Instance);
Setting a property by reflection with a string value
Ship ship = new Ship();
string value = "5.5";
PropertyInfo propertyInfo = ship.GetType().GetProperty("Latitude");
propertyInfo.SetValue(ship, Convert.ChangeType(value, propertyInfo.PropertyType), null);
Well, why not have an extension method like
public static class MyClass
{
public static string GetDefault(this str, string defaultVal)
{
return string.IsNullOrEmpty(str) ? defaultVal : str;
}
}
For a type
public class SomeClass
{
public string str = string.Empty;
}
You can call
SomeClass s = new SomeClass();
s.str.GetDefault("UNDEFINED");
You can initialize values to fields directly instead of in the constructor.
private string myStringVariable = "UNDEFINED";
Perhaps you should reconsider the structure of your program though if it permits many fields to be initialized to undefined.
Maybe I am misunderstanding this but why not do word for word what you described in the question in your constructor?
public class Weee
{
public string name { get; set; }
public int order { get; set; }
public string whatever { get; set; }
public Weee()
{
foreach(var p in typeof(Weee).GetProperties().Where(a => a.PropertyType == typeof(string)))
{
p.SetValue(this, "wut");
}
}
}
You can create a property initializer and have a base class use it. Your classes can then inherit from the base and have their properties automatically initialized:
public class PropertyInitializer
{
public void Initialize<T>(object obj, T value)
{
PropertyInfo[] properties = obj.GetType().GetProperties();
foreach (PropertyInfo property in properties)
{
if (property.PropertyType == typeof(T))
{
property.SetValue(obj, value);
}
}
}
}
public class InitializedBase
{
protected InitializedBase()
{
var initializer = new PropertyInitializer();
//Initialize all strings
initializer.Initialize<string>(this, "Juan");
//Initialize all integers
initializer.Initialize<int>(this, 31);
}
}
//Sample class to illustrate
public class AutoInitializedClass : InitializedBase
{
public string Name { get; set; }
public int Age { get; set; }
public override string ToString()
{
return string.Format("My name is {0} and I am {1} years old", Name, Age);
}
}
Sample usage:
AutoInitializedClass sample = new AutoInitializedClass();
Console.WriteLine(sample);
Console output:
My name is Juan and I am 31 years old
Notice the base class is using the PropertyInitializer class to initialize fields. This is a simplified example. You can expand it as it fits you (it may not work out of the box with all types).
I personally don't recommend this. It's called a constructor for a reason but you asked a question and I provided an answer.
Here is a simple class from which you can inherit that does exactly what you want:
Example usage:
public class MyClass : DefaultedObject<string>
{
public string MyStringField;
protected override string Default => "UNDEFINED";
}
var myClass = new MyClass();
// myClass.MyStringField == "UNDEFINED"
Implementation:
public abstract class DefaultedObject<T>
{
protected DefaultedObject()
{
T defaultValue = Default;
FieldInfo[] fields = GetType().GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
foreach(FieldInfo field in fields) {
if(field.FieldType == typeof(T)) {
field.SetValue(this, defaultValue);
}
}
}
protected abstract T Default { get; }
}
I appreciate all the feedback to this question. Here's what ended up working. First, for any string attributes in the class that I wanted to receive an automatic default value, I established as a property:
public string attribute1 {get; set;}
public string attribute2 {get; set;}
And so on. Then, in the class constructor, I included the following loop which iterates through each property of type string:
foreach(PropertyInfo property in GetType().GetProperties())
{
if (property.PropertyType == typeof(string))
property.SetValue(this, "UNDEFINED"));
}
This produced the desired outcome for me.
I'm trying to get some values from a PLC (each value has a name and a int value) and save them for backup reasons on a PC.
So i got a Struct for the name and the int
public struct sValues
{
public string ObjectName;
public int Value;
}
As there are many values that I need to store I also got a class with all of them.
public class MemMainS
{
public sValues svMode;
public sValues svFreqOrBal;
(...)
}
And a List of the Class
MemMainS mainCurrent = new MemMainS();
public List<MemMainS> TestList = new List<MemMainS>();
I also got some test values
private void SetTest()
{
mainCurrent.svMode.ObjectName = "Obj1.Addr1";
mainCurrent.svMode.Value = 1;
mainCurrent.svFreqOrBal.ObjectName = "Obj2.Addr2";
mainCurrent.svFreqOrBal.Value = 2;
}
When I try to get the data from the List with a foreach it's only possible if I tell the exact element of the List
foreach (var mv in TestList)
{
FieldInfo[] listFields = mv.GetType().GetFields(BindingFlags.Public
| BindingFlags.NonPublic
| BindingFlags.Instance);
int j = 0;
foreach (FieldInfo lf in listFields)
{
FieldInfo[] classFields = lf.FieldType.GetFields(BindingFlags.Public
| BindingFlags.NonPublic
| BindingFlags.Instance);
foreach(FieldInfo cf in classFields)
{
sValues sv;
//sv = TestList.ElementAt(j);//Possible to get it working like this?
sv = TestList.ElementAt(j).svMode;//Works for just svMode
//Output:
//Obj1.Addr1
//1
}
j++;
}
}
Am I missing something that I didn't thought of yet? Or is it even possible?
If I try sv = TestList.ElemntAt(j); VS compiler tells me that the type is not the same but the type is of sValues as sv is.
Cannot implicitly convert type 'WinFormServer.Memory.MemMainS' to
'WinFormServer.Memory.sValues'
I suggest taking advantage of the indexer properties in .Net to modify the MemMainS class to behave just a little like a Dictionary collection, like this:
public class MemMainS
{
private Dictionary<string, sValues> data = new Dictionary<string, sValues>();
public ICollection<string> Keys {get {return data.Keys;} }
public Dictionary<string, sValues>.ValueCollection.Enumerator GetEnumerator()
{
return data.Values.GetEnumerator();
}
public sValues this[string valueName]
{
get
{
if (data.ContainsKey(valueName)) return data[valueName];
return default(sValues); // could opt to throw an exception here instead
}
set
{
data[valueName] = value;
}
}
public sValues svMode {get {return data["svMode"]; } set {data["svMode"] = value;} }
public sValues svFreqOrBal {get {return data["svFreqOrBal "]; } set {data["svFreqOrBal "] = value;} };
// (...)
}
That will let you re-write the loop like this to avoid reflection:
foreach (var mv in TestList)
{
foreach(string item in mv.Keys)
{
sValues sv = mv[item];
}
}
Or like this:
foreach(var mv in TestList)
{
foreach(sValues sv in mv)
{
//...
}
}
The problem here is we don't know which sValue property we're looking at in those loops. We have the ObjectName address, but not the property name. It seems like those property names should be included in the struct data. You have a property name, like svFreqOrBal, that will have a 1:1 mapping to a ObjectName or address like Obj2.Addr2. There's no reason not to include that as part of the struct.
While I'm here, I suggest defining the struct using a simple immutable pattern this way:
public struct sValues
{
public string ObjectName {get;private set;}
public int Value {get; private set;}
public sValues(string objectName, int Value)
{
ObjectName = objectName;
this.Value = Value;
}
}
(Of course, with the potential addition of the name property as suggested above).
Finally, seeing in the comments you have more than 1000 potential different PLC values, you may want to forgo the named properties in MemMainS entirely, and only use the Dictionary + indexer. Of course, this costs you some compile-time safety, but you can make up for some of this by getting a list or string[] of your valid PLC value names you can check against in the indexer for validation.
You list a List<MemMainS>; therefore the j'th element of the list is a MemMainS. You are trying to assign it to sv, a sValues value. That doesn't work by default; you can add an implicit conversion operator, but you really probably shouldn't. But if you search "C# overload implicit conversion operator" it'll show you how. It is much easier to just access the .svMode member of the j'th element.
I have a complex class hierarchy: class B is an attribute of class A, List(class C) and class D are attributes of class B, etc - lots of levels of parent-child relationship.
Some classes in the hierarchy has string attribute "foobar". Some classes don't.
I have an instanse of class A. I need to find all objects in the hierarchy which has attribute "foobar", and change its value to "qwerty".
Is there a simple way to do that in C#?
public class ClassD
{
public string fooBar;
}
public class ClassC
{ }
public class ClassB
{
public List<ClassC> classCList;
public ClassD classDInstance;
public string fooBar;
}
public class ClassA
{
public ClassB classBInstance;
}
You can use Reflection, although I'm not a big fan of such, I'd guess that is the easiest way to acomplish what you need.
The code below is not very pretty, but hopefully will give you an idea.
private static void replaceFoobar(object obj)
{
if (obj == null)
return;
const BindingFlags bindingFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.GetField | BindingFlags.GetProperty | BindingFlags.Instance;
FieldInfo[] members = obj.GetType().GetFields(bindingFlags);
if (!members.Any())
return;
foreach (var item in members)
{
if (item.Name == "fooBar" || item.Name == "<fooBar>k__BackingField")
{
item.SetValue(obj, "qwerty");
}
else
{
object value = item.GetValue(obj);
if (value != null && value is IEnumerable)
{
foreach (var itemE in ((IEnumerable)value))
replaceFoobar(itemE);
}
else
replaceFoobar(value);
}
}
}
It looks like you want to change the value of an attribute with a given name.
This is pretty ugly but might give you some idea how to accomplish what you want using reflection:
static void Set<T>(dynamic obj, string property, T value) {
//Iterate through the fields on your object. Can change this to properties or do both.
foreach (var field in obj.GetType().GetFields(BindingFlags.Public | BindingFlags.Instance)) {
var fieldValue = field.GetValue(obj);
//If we have a collection, then iterate through its elements.
if (field.FieldType.GetInterface("System.Collections.IEnumerable") != null && !(fieldValue is string) && fieldValue != null)
foreach (var item in fieldValue) SetField<T>(item, property, value);
//If field name and type matches, then set.
else if (field.Name == property && field.FieldType == typeof(T)) field.SetValue(obj, value);
}
}
You'd call it as follows:
Set(obj, "fooBar", "qwerty");
Note that this only iterates through fields at the moment because that is how your classes are set up (public fields, not properties). If you want to include properties, you can change it to work with properties, or combine fields and properties and iterate through both.
Again, I'm not saying to use this approach, but might give you some ideas.
EDIT: I had an error in my unit test. So my question is invalid. I have marked the correct answer below and the question should be "How do I get private members on inherited classes".
I traverse the class hierarchy to get the private members I need.
I am using reflection to get the members of a class hierarchy. Originally, I was pulling properties and fields separately. I refactored my code just to get MemberInfo and started running into a small issue.
Originally I was able to get all the fields using:
type.GetFields(BindingFlags.Public | BindingFlags.Instance | BindingFlags.FlattenHierarchy | BindingFlags.NonPublic)
This would return all public and private fields in the class hierarchy.
For example on the classes:
class ViewModel {
[MetaData("MetaFieldType1", typeof(Parent), null)]
public string field1;
[MetaData("MetaFieldType2", typeof(Parent), "Field Value 2")]
private string field2;
[MetaData("MetaProtectedField", typeof(Parent), "Protected Field")]
protected string pField;
protected string pField2;
[MetaData("MetaType 1", typeof(Parent), null)]
public String Prop1 { get; set; }
[MetaData("MetaType 2", typeof(Parent), "The Value")]
public String Prop2 { get; set; }
public string Field2 { get { return field2; } set { field2 = value; } }
}
class ViewModel2 : ViewModel {
public string PField { get { return pField; } set { pField = value; } }
[MetaData("MetaProtectedField2", typeof(Parent), null)]
public string PField2 { get { return pField2; } set { pField2 = value; } }
}
4 fields would be returned on both ViewModel and ViewModel2
I then changed the code to use (rather than getting properties then fields):
type.FindMembers(MemberTypes.Field | MemberTypes.Property,
BindingFlags.FlattenHierarchy | BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic,
allMemberFilter,null)
Where
protected static readonly MemberFilter allMemberFilter = new MemberFilter(
delegate (MemberInfo objMemberInfo, Object objSearch) {
return true;
});
and ViewModel returns all the properties and fields; however, ViewModel2 is missing the private fields (i.e. field2) found in ViewModel. This was not the case when getting them using the GetFields and GetProperties with the same binding flags.
If I change field2 to protected, then it shows up in the search.
Is this an issue with the framework or am I missing something?
First of all, private members of a base Type are considered members of the inherited Type. from C# Language Specification Version 5.0:
A class inherits the members of its base class. Inheritance means that a class implicitly contains all members of its base class (A.k.a: Including the private members of the base class), except for the instance and static constructors, and the destructors of the base class.
If you inspect the BindingFlags.FlattenHierarchy documentation, you will see it refers only to static members:
Specifies that public and protected static members up the hierarchy should be returned.
With that said, apparently, and unfortunately the only way to get private members of a Type is using reflection on their declaring Type, which means, in our case - the base Type itself.
So we will have to write an extension to do so recursively:
static class TypeExtensions
{
public static IEnumerable<MemberInfo> GetAllInstanceMembers(this Type type)
{
if (type == null || type == typeof (object) || !type.IsClass || type.IsInterface)
{
return Enumerable.Empty<MemberInfo>();
}
IEnumerable<MemberInfo> baseMembers = type.BaseType.GetAllInstanceMembers();
IEnumerable<MemberInfo> interfacesMembers = type.GetInterfaces().SelectMany(x => x.GetAllInstanceMembers());
IEnumerable<MemberInfo> currentMembers = type.FindMembers(MemberTypes.Field | MemberTypes.Property,
BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic,
AllMemberFilter, null);
return baseMembers.Concat(interfacesMembers).Concat(currentMembers);
}
static readonly MemberFilter AllMemberFilter = (objMemberInfo, objSearch) => true;
}
Note that apparently BindingFlags.DeclaredOnly makes the method return the backing fields generated by the constructor for the auto-properties while in the question's example they are not returned, this can be fixed by changing the return line to:
return baseMembers.Concat(interfacesMembers).Concat(currentMembers)
.Where(x => x.GetCustomAttribute<CompilerGeneratedAttribute>() == null);
Also, I must say that I am getting the same results for GetMembers, FindMembers and GetProperties/GetFields
One class has a field. Second class should be able to reference that field and to change it. Since you cannot get the address of a field in memory, I have to use reflections. But my approach only works on non-encapsulated fields. So basically:
This works:
public class Dummy
{
public int field;
public Dummy(int value)
{
this.field = value;
}
}
class Program
{
public static void Main()
{
Dummy d = new Dummy(20);
//Shows 20
Console.WriteLine(d.field.ToString());
d.GetType().GetField("field", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance).SetValue(d, 40);
//It should show 40 now
Console.WriteLine(d.field.ToString());
}
}
This doesn't (throws NullReferenceException):
public class Dummy
{
public int field { get; set; }
public Dummy(int value)
{
this.field = value;
}
}
class Program
{
public static void Main()
{
Dummy d = new Dummy(20);
//Shows 20
Console.WriteLine(d.field.ToString());
d.GetType().GetField("field", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance).SetValue(d, 40);
//It should show 40 now
Console.WriteLine(d.field.ToString());
}
}
Why? How do I fix it? Can I even access an encapsulated object like that? Thank you!
It is not a field any longer. Instead of GetField you should use GetProperty method:
d.GetType().GetProperty(...);
In both cases, you could (and should) write
d.field = 40;
When you want a field to be non-private, you should always make it a property, as in your second example. You should never have public fields. Note that if you need to access a property via reflection, you'd use GetProperty, not GetField.
Can you be more precise about the use case?
Maybe an interface that isolates the properties' accessors is better:
public interface IDummy
{
int field { get; set; }
}
You'll be able to manipulate the property without knowing anything about the rest of the object. Isn't that what you need when you want to "reference" a single field?