C# Accessing field syntax - c#

How would I access an object's variable's value if I only have the name of the variable I wish to access? In C#.
Say I had a list of variable names represented as strings in an array.
How would I access them in a loop, for example.
I can do something like the following in Actionscript:
var arrayOfVariableNames:Array = ["name", "age", "sex"]
for each(var person:Person in persons)
{
if (person[age] > 29) //what is equivalent in c# for object[field]
{
//do something
}
}

You can use reflection to access a field by its name :
FieldInfo ageField = typeof(Person).GetField("age");
int age = (int) field.GetValue(person);

The only way to get every variable in a list of variables is reflection over the object, however, you would end up with a group of values of type object, with no way of knowing the type of what's actually contained there (ie. you'd end up with a variable of type object for Name [string], Age [int], and Weight [int]). This makes reflection a poor way of getting a set of values from an object.
However, the general syntax to access a field is object.value, like this:
Person p = new Person ("John", 25, 160); // Name, age, weight (lbs)
Console.WriteLine ("Hello {0}!", p.Name); // Output: "Hello John!"
Note that this usage of Console.WriteLine is pretty much the same as using printf/fprintf in C and its ilk.

If I correctly understood your question, you could do something like this:
class Person {
private int age;
private string name;
private string sex;
public object this[string name]
{
get
{
PropertyInfo property = GetType().GetField(name);
return property.GetValue(this, null);
}
set
{
PropertyInfo property = GetType().GetField(name);
property.SetValue(this, value, null);
}
}
}
It would solve your problem, but if my opinion matters, you should use normal properties instead, as you would lose type safety.

Related

Why the tuple-type list element's value cannot be modified?

In C# 8.0, I can modify the value inside a tuple directly by accessing the field name:
(string name, int score) student = ("Tom", 100);
student.name = "Jack";
Console.WriteLine(student);
And I can modify the list element's property as follow:
var list = new List<Student>(); // assume I have a Student class which has a Name property
list.Add(new Student { Name = "Tom" });
list[0].Name = "Jack";
Console.WriteLine(list[0]);
But why can't I modify the tuple-type element's value like this?
var list = new List<(string name, int score)>();
list.Add(("Tom", 100));
list[0].name = "Jack"; // Error!
Console.WriteLine(list[0]);
A tuple (ValueTuple) is a struct. Rather than returning a reference to the value as is the case with your Student example, you would actually recieve a copy of the tuple.
Changes to that copy wouldn't be reflected in the list and would be discarded. The compiler is smart enough to recognize this and stops you from doing it.
If it did compile, it would be to something similar to the following:
var list = new List<(string name, int score)>(); list.Add(("Tom", 100));
var copy = list[0];
copy.name = "Jack";
Console.WriteLine(copy.name); // Jack
Console.WriteLine(list[0].name); // still Tom
Mutable structs can be dangerous if you don't use them properly. The compiler is simply doing its job.
You can work around this with the following:
var list = new List<(string name, int score)>(); list.Add(("Tom", 100));
var copy = list[0];
copy.name = "Jack";
list[0] = copy; // put it back
Console.WriteLine(copy.name); // Jack
Console.WriteLine(list[0].name); // Jack
Try It Online
If you use an array (string, int)[] instead of a List<(string, int)>, this isn't a problem due to the way array element access works:
var arr = new (string name, int score) [] { ( "Tom", 10 ) };
arr[0].name = "Jack";
Console.WriteLine(arr[0].name); // Jack
Try It Online
This behavior is not unique to List or your tuple type. You'll experience this issue with any collection where the element is a Value Type (unless of course they offer a ref element accessor).
Note that there are similar issues when having a readonly field of a mutable value type that mutates via method calls. This can be much more insidious as no error or warning is emitted:
struct MutableStruct {
public int Val;
public void Mutate(int newVal) {
Val = newVal;
}
}
class Test {
private readonly MutableStruct msReadonly;
private MutableStruct msNormal;
public Test() {
msNormal = msReadonly = new MutableStruct(){ Val=5 };
}
public void MutateReadonly() {
Console.WriteLine(msReadonly.Val); // 5
msReadonly.Mutate(66); // defensive copy!
Console.WriteLine(msReadonly.Val); // still 5!!!
}
public void MutateNormal() {
Console.WriteLine(msNormal.Val); // 5
msNormal.Mutate(66);
Console.WriteLine(msNormal.Val); // 66
}
}
new Test().MutateReadonly();
new Test().MutateNormal();
Try It Online
ValueTuple is a great addition to the framework and language. But there's a reason you'll often hear that [Mutable] structs are evil. In the majority of cases you shouldn't hit these restrictions. If you find yourself falling into this pattern a lot, I suggest moving over to a record, which is a reference type (thus not suffering these issues) and can be reduced to a tuple-like syntax.
Mutable value types are evil, it's hard to see why this prints "Tom" not "Jack":
(string name, int score) student = ("Tom", 100);
(string name, int score) student2 = student;
student.name = "Jack";
Console.WriteLine(student2);
The reason is that you always create a copy. Because it's not obvious you should avoid mutable value types. To avoid that people will fall into that trap the compiler just allows to modify the object directly via properties(like above). But if you try to do it via a method call you get a compiler error "Cannot modify the return value of ... because it is not a variable".
So this is not allowed:
list[0].name = "Jack";
It would create a new copy of the ValueTuple, assigns a value but doesn't use or store it anywhere.
This compiles because you assign it to a new variable and modify it via property:
(string name, int score) x = list[0];
x.name = "Jack"; // Compiles
So it compiles but gives you again a suprising result:
Console.WriteLine(list[0]); // Tom
Read more about it here: Do Not Define Mutable Value Types

How to use a variable to access an object member in c#

I have a web service that returns a custom object (a struct type) APIStruct user.
I have a variable holding the name of the current field it is checking for currentField.
Now, in a situation where user contains first_name and last_name, I can access the value using user.first_name or user.last_name. BUT, is it possible to hold the field name in a variable and access the value through the variable? working like:
var currentField = "first_name";
var value = user.currentField;
Obviously the above is not working, so is there any way to do this? In the past with languages such as PowerShell it works just like above $currentField = "first_name"; $value = user.$currentField
I've tried user.currentField user.(currentField) user[currentField] user.$currentField
You can extend your object class to support access to a Dictionary of additional properties, accessible through an explicit indexer.
public class myClass
{
private Dictionary<string, object> Something = new Dictionary<string, object>();
public object this[string i]
{
get { return Something[i]; }
set { Something[i] = value; }
}
}
Use like this:
myClass m = new myClass();
Set value:
m["fist name"] = "Name";
m["level"] = 2;
m["birthday"] = new DateTime(2015, 1, 1);
Get value:
int level = (int)m["level"];
string firstName = (string)m["first name"];
DateTime dt = (DateTime)m["birthday"];
you must use reflection. Create a method like this:
public static object GetPropValue(object src, string propName)
{
return src.GetType().GetProperty(propName).GetValue(src, null);
}
and call it:
string currentField = "first_name";
GetPropValue(user, currentField);
But it must be said, it is not the way you should use for the standard reading of object values.
What you're looking for called Reflection.
var type = user.GetType(); // Get type object
var property = type.GetProperty(currentField); // get property info object
var value = property.GetValue(user); // get value from object.
Be careful - reflection is pretty slow compared to direct property access.
Another option you may have is to use a switch statement. Something like:
switch (currentField){
case "first_name":
value = user.first_name;
break;
case "last_name":
value = user.last_name;
break;
etc...

Assign object attribute to a String variable in SAP using c#

Imagine I have created an object name of oItem. And this has a lot of attributes such as ItemCode, ItemName.....(around 300). And I want to assign new values to some of these attributes which were selected by the user of my application. User will give these attribute names as strings.
Ex: string attribute1 = "ItemCode".
Now what I want to do is assign a value to this attribute like:
oItem.attribute1 = "01234";
Is there a way to do something like this? I know you can convert a c# function call to a string. Therefore I think this should be possible too. Any help would be highly appreciated. Thanks!
UPDATE: This is a part of my SAP add-On. So these attributes are from a database table. The hard part is user can add more columns(user defined fields) to this which increase the number of attributes as well as I only know the original 300 attributes.
If you have 300 properties you should really refactor this class. I think that you can use a Dictionarystring, string> in this case.
Dictionary<string, string> Items = new Dictionary<string, string>
{
{"attribute1", "01234"}, {"attribute2", "56789"}, {"attribute3", "76543"}, // ...
};
You can access the values very efficiently:
string attribute1 = Items["attribute1"];
or add/modify them in a similar way:
Items["attribute4"] = "23456";
To make it easier for you there's a "dynamic" keyword in C#.
A sample from msdn dynamic object article:
// The class derived from DynamicObject.
public class DynamicDictionary : DynamicObject
{
// The inner dictionary.
Dictionary<string, object> dictionary
= new Dictionary<string, object>();
// This property returns the number of elements
// in the inner dictionary.
public int Count
{
get
{
return dictionary.Count;
}
}
// If you try to get a value of a property
// not defined in the class, this method is called.
public override bool TryGetMember(
GetMemberBinder binder, out object result)
{
// Converting the property name to lowercase
// so that property names become case-insensitive.
string name = binder.Name.ToLower();
// If the property name is found in a dictionary,
// set the result parameter to the property value and return true.
// Otherwise, return false.
return dictionary.TryGetValue(name, out result);
}
// If you try to set a value of a property that is
// not defined in the class, this method is called.
public override bool TrySetMember(
SetMemberBinder binder, object value)
{
// Converting the property name to lowercase
// so that property names become case-insensitive.
dictionary[binder.Name.ToLower()] = value;
// You can always add a value to a dictionary,
// so this method always returns true.
return true;
}
}
class Program
{
static void Main(string[] args)
{
// Creating a dynamic dictionary.
dynamic person = new DynamicDictionary();
// Adding new dynamic properties.
// The TrySetMember method is called.
person.FirstName = "Ellen";
person.LastName = "Adams";
// Getting values of the dynamic properties.
// The TryGetMember method is called.
// Note that property names are case-insensitive.
Console.WriteLine(person.firstname + " " + person.lastname);
// Getting the value of the Count property.
// The TryGetMember is not called,
// because the property is defined in the class.
Console.WriteLine(
"Number of dynamic properties:" + person.Count);
// The following statement throws an exception at run time.
// There is no "address" property,
// so the TryGetMember method returns false and this causes a
// RuntimeBinderException.
// Console.WriteLine(person.address);
}
}
// This example has the following output:
// Ellen Adams
// Number of dynamic properties: 2
It is really easy to do this for SAP B1 addon using c# with the provided library functions. Simply include using SAPbobsCOM;
And then you can do it as follows
yourobject.UserFields.Fields.Item("attribute").Value = "value";

How to iterate properties of two objects dynamically

I have two object of same class, I want to update the p2 with fields which are are in Dirty list. So far I managed to write the following code but struggling to get the value of p1 properties. What object should I pass here as parameter to GetValue method.
Person p1 = new Person();
p1.FirstName = "Test";
Person p2 = new Person();
var allDirtyFields = p1.GetAllDirtyFields();
foreach (var dirtyField in allDirtyFields)
{
p2.GetType()
.GetProperty(dirtyField)
.SetValue(p1.GetType().GetProperty(dirtyField).GetValue());
}
_context.UpdateObject(p2);
_context.SaveChanges();
Thanks in advance.
You should try that:
foreach (var dirtyField in allDirtyFields)
{
var prop = p2.GetType().GetProperty(dirtyField);
prop.SetValue(p2, prop.GetValue(p1));
}
It is a better to store PropertyInfo instance in a variable, then trying to resolve it twice.
Did you know that you don't need to retrieve the property for each object?
Type metadata is common to any object of the whole type.
For example:
// Firstly, get dirty property informations!
IEnumerable<PropertyInfo> dirtyProperties = p2.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public)
.Where
(
property => allDirtyFields.Any
(
field => property.Name == field
)
);
// Then, just iterate the whole property informations, but give the
// "obj" GetValue/SetValue first argument the references "p2" or "p1" as follows:
foreach(PropertyInfo dirtyProperty in dirtyProperties)
{
dirtyProperty.SetValue(p2, dirtyProperty.GetValue(p1));
}
Check that the first parameter of PropertyInfo.GetValue(...) and PropertyInfo.SetValue(...) is the object for which you want to get or set the value of the whole property.
In each iteration, you have to get a reference to the PropertyInfo. When you call it's SetValue method, you should pass in 2 parameters, the object for which you will set the property and the actual value you are setting. For the latter one, you should invoke the GetValue method on the same property, passing in the p1 object as parameter, i.e. the source for the value.
Try this:
foreach (var dirtyField in allDirtyFields)
{
var p = p2.GetType().GetProperty(dirtyField);
p.SetValue(p2, p.GetValue(p1));
}
I would recommend you to keep the dirtyField variables in a dictionary and retrieve the associated PropertyInfo object from this dictionary. It should be much faster.
Firstly, declare some static variable in your class:
static Dictionary<string, PropertyInfo>
personProps = new Dictionary<string, PropertyInfo>();
Then you may change your method to:
foreach (var dirtyField in allDirtyFields)
{
PropertyInfo p = null;
if (!personProps.ContainsKey(dirtyField))
{
p = p2.GetType().GetProperty(dirtyField);
personProps.Add(dirtyField, p);
}
else
{
p = personProps[dirtyField];
}
p.SetValue(p2, p.GetValue(p1));
}
You need to pass the instance from which you want to get the property value, like so:
p1.GetType().GetProperty(dirtyField).GetValue(p1, null)
The second parameter, can be used to retrieve a value at a certain index if the property type is indexed.
IIrc you send p1 being the instance that holds the value and null to indicate you're not searching for a specific index value.

Looping though a dictionary containing objects

I have the following loop over a dictionary type collection
foreach(KeyValuePair<Vector2, Object> entry in v_map.map_set)
{
}
I want to access the object properties, but the expected syntax doesn't work. E.G:
foreach(KeyValuePair<Vector2, Object> entry in v_map.map_set)
{
Object ob = entry.Value;
ob.property;
}
Fails because C# can't find the property wanted.
So, how do I access the desired properties?
solution:
foreach(KeyValuePair<Vector2, Object> entry in v_map.map_set)
{
if (entry.Value is warehouse)
{
warehouse ob = (warehouse)entry.Value;
}
}
If you know the type of the objects that are in the KeyValuePair, you can cast it to that type, and you will be able to find the properties you need.
And if you have several different objects stored, you can check which type it is by using is.
Like so:
if(entry.Value is Foo)
{
Foo lFoo = (Foo)entry.Value;
}
else if(entry.Value is Bar)
{
Bar lBar = (Bar)entry.Value;
}
You can make use of Refection to get the value of proerty of the object.
something like this
PropertyInfo info2 = object.GetType().GetProperty("prpertyname");
Object val = info2.GetValue(object, null);
You need to cast entry.Value to the type you need. The Object type itself isn't going to expose the properties you want.
If you just need to access the values, and you know the expected type you can use
foreach(ExpectedType value in v_map.map_set.Values.OfType<ExpectedType>())
{
var property = value.Property;
}
where Property is a property on ExpectedType.
The problem is that you're using an object which isn't typed. So you're going to need to use reflection like this:
PropertyInfo pi = ob.GetType().GetProperty("PropertyName");
var val = pi.GetValue(ob, null);
Now, if the property isn't public then you'll need to employ something else like this:
PropertyInfo pi = ob.GetType().GetProperty("PropertyName", BindingFlags.Instance | BindingFlags.NonPublic);
var val = pi.GetValue(ob, null);
Now, if this is actually a field you're trying to get to, you're going to need to do something different even yet:
FieldInfo fi = ob.GetType().GetField("fieldName");
var val = fi.GetValue(ob);
GetProperty method
BindingFlags enumeration
GetField method

Categories