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...
Related
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
In SharePoint Server side code, you can write something like:
field.fieldvalueType
Which will sometimes give you the type (DateTime, or whatever). Annoyingly, sometimes, it just returns Null (for example, the ID field).
In CSOM, you don't have that field. However, there's TypeAsString which gives SharePoint types such as:
Computed
Integer
Note
What I'd like to do is grab this huge table from MSDN:
And Extract "Int32" when I know that I'm dealing with an "Integer" field, and extract "System.String" from SharePoint's note.
This kinda works, but it's the mother of all hacks:
var myTempItem = list.AddItem(new ListItemCreationInformation());
myTempItem.Update();
context.ExecuteQuery();
context.Load(myTempItem);
context.ExecuteQuery();
After creation, you can use:
myTempItemCreated[fieldImTryingToGetTypeOf.Title].GetType().FullName -> Gives-> System.Int32
Now, what's the proper way to do it? I'm just hoping the answer isn't a ten foot long switch case statement.
Since there is no SPField.FieldValueType property counterparts in SharePoint CSOM API, the following extension method demonstrates how to perform it:
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.SharePoint.Client;
using Field = Microsoft.SharePoint.Client.Field;
namespace SharePoint.Client.Extensions
{
public static class FieldExtensions
{
public static Type GetFieldValueType(this Field field)
{
var table = new Dictionary<FieldType, Type>();
table[FieldType.Guid] = typeof(Guid);
table[FieldType.Attachments] = typeof(bool);
table[FieldType.Boolean] = typeof(bool);
table[FieldType.Choice] = typeof (string);
table[FieldType.CrossProjectLink] = typeof(bool);
table[FieldType.DateTime] = typeof(DateTime);
table[FieldType.Lookup] = typeof(FieldLookupValue);
table[FieldType.ModStat] = typeof(int);
table[FieldType.MultiChoice] = typeof(string[]);
table[FieldType.Number] = typeof(double);
table[FieldType.Recurrence] = typeof(bool);
table[FieldType.Text] = typeof(string);
table[FieldType.URL] = typeof(FieldUrlValue);
table[FieldType.URL] = typeof(FieldUrlValue);
table[FieldType.User] = typeof(FieldUserValue);
table[FieldType.WorkflowStatus] = typeof(int);
table[FieldType.ContentTypeId] = typeof(ContentTypeId);
table[FieldType.Note] = typeof(string);
table[FieldType.Counter] = typeof(int);
table[FieldType.Computed] = typeof(string);
table[FieldType.Integer] = typeof(int);
table[FieldType.File] = typeof(string);
if (!table.ContainsKey(field.FieldTypeKind))
throw new NotSupportedException(string.Format("Unknown field type: {0}", field.FieldTypeKind));
return table[field.FieldTypeKind];
}
}
}
Usage
var list = ctx.Web.Lists.GetByTitle(listTitle);
var fields = list.Fields;
ctx.Load(fields);
ctx.ExecuteQuery();
foreach (var field in fields)
{
if (field.FieldTypeKind != FieldType.Invalid)
{
var fieldValueType = field.GetFieldValueType();
Console.WriteLine("{0} : {1}", field.InternalName, fieldValueType);
}
}
Expanding on #Vadim's answer, here is a version that does not construct a new dictionary every time the extension method is called;
namespace SharePoint.Client.Extensions
{
public static class FieldExtensions
{
private static Dictionary<FieldType, Type> _fieldTypes = new Dictionary<FieldType, Type>()
{
{ FieldType.Guid, typeof(Guid) },
{ FieldType.Attachments, typeof(bool)},
{FieldType.Boolean, typeof(bool)},
{FieldType.Choice, typeof(string)},
{FieldType.CrossProjectLink, typeof(bool)},
{FieldType.DateTime, typeof(DateTime)},
{FieldType.Lookup, typeof(FieldLookupValue)},
{FieldType.ModStat, typeof(int)},
{FieldType.MultiChoice, typeof(string[])},
{FieldType.Number, typeof(double)},
{FieldType.Recurrence, typeof(bool)},
{FieldType.Text, typeof(string)},
{FieldType.URL, typeof(FieldUrlValue)},
{FieldType.User, typeof(FieldUserValue)},
{FieldType.WorkflowStatus, typeof(int)},
{FieldType.ContentTypeId, typeof(ContentTypeId)},
{FieldType.Note, typeof(string)},
{FieldType.Counter, typeof(int)},
{FieldType.Computed, typeof(string)},
{FieldType.Integer, typeof(int)},
{FieldType.File, typeof(string)}
};
public static Type GetFieldValueType(this Field field)
{
if (!_fieldTypes.ContainsKey(field.FieldTypeKind))
throw new NotSupportedException(string.Format("Unknown field type: {0}", field.FieldTypeKind));
return _fieldTypes[field.FieldTypeKind];
}
}
}
Generally, you need to do the mapping you describe, not the myTempItemCreated[fieldImTryingToGetTypeOf.Title].GetType().FullName
method.
The reason is, myTempItemCreated[fieldImTryingToGetTypeOf.Title].GetType().FullName
will fail in the scenario where the field value (e.g. Title) is null for that particular ListItem object. (While Title is not generally null, other fields can be and are null). And obviously null does not give you an object on which you can call the GetType() method (you would get a NullReferenceException obviously).
So, for a general solution to the problem, one does indeed have to map the String returned from TypeAsString of the list field, calling from the list object / list field, and not from the list item.
You can get the type of field using the following snippet:
item.Fields["Title"].FieldValueType.FullName
var myObject = new { Id = 0, Name = "Test" };
I would like to get key name into variable in other part of project for ex.:
var keyName = someObject[0]; //someObject is passed myObject;
var value = someObject[1];
//Output keyName = Id; value = 0;
I know that dictionary would be good, but I have to stick to the delaration of myObject;
You can access the properties of your object this way:
myObject.GetType().GetProperties()
and the names e.g.
myObject.GetType().GetProperties()[0].Name //("Id")
It is not possible to do this with reflection, because variables won't have a name once compiled to IL. However, you can use expression trees and promote the variable to a closure:
static string GetVariableName<T>(Expression<Func<T>> expr)
{
var body = (MemberExpression)expr.Body;
return body.Member.Name;
}
You can use this method as follows:
static void Main()
{
var someVar = 3;
Console.Write(GetVariableName(() => someVar));
}
Note that this is pretty slow, so don't use it in performance critical paths of your application.
For a more complete example, see here.
I'm writing an application that runs "things" to a schedule.
Idea being that the database contains assembly, method information and also the parameter values. The timer will come along, reflect the method to be run, add the parameters and then execute the method.
Everything is fine except for the parameters.
So, lets say the method accepts an ENUM of CustomerType where CustomerType has two values of CustomerType.Master and CustomerType.Associate.
EDIT
I don't know the type of parameter that will be getting passed in. ENUM used as an example
END OF EDIT
We want to run Method "X" and pass in parameter "CustomerType.Master". In the database, there will be a varchar entry of "CustomerType.Master".
How do I convert the string "CustomerType.Master" into a type of CustomerType with a value of "Master" generically?
Thanks in advance,
Jim
OK, the scope of the question shifted but my original observation and objection to some other solutions still stands.
I think you don't/can't want to use 'generics' here. You don't know the type ahead of time, and since you will need to create the type, there is no need to use a generic implementation because MethodBase.Invoke takes an array of Object.
This code assumes you are instantiating the target from database field. If not just adjust accordingly.
Of course this is not all encompassing and has no useful exception handling, but it will allow you to dynamically execute arbitrary methods on an arbitrary type with arbitrary parameters values all coming from string values in a row.
NOTE: there are many many many scenarios in which this simple executor will not work. You will need to ensure that you engineer your dynamic methods to cooperate with whatever strategy you do end up deciding to use.
using System;
using System.ComponentModel;
using System.Drawing;
using System.Globalization;
using System.Reflection;
using NUnit.Framework;
namespace DynamicMethodInvocation
{
[TestFixture]
public class Tests
{
[Test]
public void Test()
{
// from your database
string assemblyQualifiedTypeName = "DynamicMethodInvocation.TestType, DynamicMethodInvocation";
string methodName = "DoSomething";
// this is how you would get the strings to put in your database
string enumString = Executor.ConvertToString(typeof(AttributeTargets), AttributeTargets.Assembly);
string colorString = Executor.ConvertToString(typeof(Color), Color.Red);
string stringString = "Hmm... String?";
object result = Executor.ExecuteMethod(assemblyQualifiedTypeName, methodName,
new[] { enumString, colorString, stringString });
Assert.IsInstanceOf<bool>(result);
Assert.IsTrue((bool)result);
}
}
public class TestType
{
public bool DoSomething(AttributeTargets #enum, Color color, string #string)
{
return true;
}
}
public class Executor
{
public static object ExecuteMethod(string assemblyQualifiedTypeName, string methodName,
string[] parameterValueStrings)
{
Type targetType = Type.GetType(assemblyQualifiedTypeName);
MethodBase method = targetType.GetMethod(methodName);
ParameterInfo[] pInfo = method.GetParameters();
var parameterValues = new object[parameterValueStrings.Length];
for (int i = 0; i < pInfo.Length; i++)
{
parameterValues[i] = ConvertFromString(pInfo[i].ParameterType, parameterValueStrings[i]);
}
// assumes you are instantiating the target from db and that it has a parameterless constructor
// otherwise, if the target is already known to you and instantiated, just use it...
return method.Invoke(Activator.CreateInstance(targetType), parameterValues);
}
public static string ConvertToString(Type type, object val)
{
if (val is string)
{
return (string) val;
}
TypeConverter tc = TypeDescriptor.GetConverter(type);
if (tc == null)
{
throw new Exception(type.Name + " is not convertable to string");
}
return tc.ConvertToString(null, CultureInfo.InvariantCulture, val);
}
public static object ConvertFromString(Type type, string val)
{
TypeConverter tc = TypeDescriptor.GetConverter(type);
if (tc == null)
{
throw new Exception(type.Name + " is not convertable.");
}
if (!tc.IsValid(val))
{
throw new Exception(type.Name + " is not convertable from " + val);
}
return tc.ConvertFrom(null, CultureInfo.InvariantCulture, val);
}
}
}
I would think you have 2 major options:
Store the type name along with the parameter value and use that to cast things using Type.GetType(string) to resolve the type in question.
Standardize all the methods to be called this way to accept an array of strings, and expect the methods to do any necessary casting.
I know you've stated that you're not doing option 1, but it would help things from the standpoint of calling the functions.
Option 2 is the far more 'generic' way to handle the situation, assuming all values can be represented by and cast/converted from strings to the appropriate type. Of course, that only helps if you actually have control over the definition of the methods being called.
Below is a useful extension method I use in .NET 3.5.
With this extension method available, your code could look like this:
var valueInDb = GetStringFromDb().Replace("CustomerType.", string.Empty);
var value = valueInDb.ToEnum(CustomerType.Associate);
By supplying the default value in the parameter, the compiler will know which Enum you want your string to be turned into. It will try to find your text in the Enum. If it doesn't it will return the default value.
Here is the extension method: (this version also does partial matches, so even "M" would work nicely!)
public static T ToEnum<T>(this string input, T defaultValue)
{
var enumType = typeof (T);
if (!enumType.IsEnum)
{
throw new ArgumentException(enumType + " is not an enumeration.");
}
// abort if no value given
if (string.IsNullOrEmpty(input))
{
return defaultValue;
}
// see if the text is valid for this enumeration (case sensitive)
var names = Enum.GetNames(enumType);
if (Array.IndexOf(names, input) != -1)
{
// case insensitive...
return (T) Enum.Parse(enumType, input, true);
}
// do partial matching...
var match = names.Where(name => name.StartsWith(input, StringComparison.InvariantCultureIgnoreCase)).FirstOrDefault();
if(match != null)
{
return (T) Enum.Parse(enumType, match);
}
// didn't find one
return defaultValue;
}
I still don't fully understand your question... however, you say "Everything is fine except for the parameters."
I'll assume "CustomerType" the name of a property on your object, and "Master" is the string value you want to put in that property.
Here is (another) extension method that may help.
Once you have your new object and the value and property name from the database field, you could use this:
// string newValue = "Master";
// string propertyName = "CustomerType";
myNewObject.SetPropertyValue(propertyName, newValue)
Method:
/// <summary>Set the value of this property, as an object.</summary>
public static void SetPropertyValue(this object obj,
string propertyName,
object objValue)
{
const BindingFlags attr = BindingFlags.Public | BindingFlags.Instance;
var type = obj.GetType();
var property = type.GetProperty(propertyName, attr);
if(property == null) return;
var propertyType = property.PropertyType;
if (propertyType.IsValueType && objValue == null)
{
// This works for most value types, but not custom ones
objValue = 0;
}
// need to change some types... e.g. value may come in as a string...
var realValue = Convert.ChangeType(objValue, propertyType);
property.SetValue(obj, realValue, null);
}
If you are using .NET 4 you can do the following.
var result = default(CustomerType);
if (!Enum.TryParse("Master", out result))
{
// handle error
}
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.