C# access object properties at runtime - c#

I'm calling a web service that is returning this object. I know I should be using object reflection in C# to access the property of sentBatchTotal. However, I can't for the life of me figure out how to get to this property. I have looked at several other articles here and on MSDN but it's just not sinking in.
Here is my code, what am I doing wrong?
private void button1_Click(object sender, EventArgs e)
{
prdChal.finfunctions service = new prdChal.finfunctions();
//Type thisObject = typeof()
//Type myType = myObject.GetType();
//IList<PropertyInfo> props = new List<PropertyInfo>(myType.GetProperties());
String ThisName = "";
Object StatusReturn = new Object();
StatusReturn = service.UpdateGrantBillStatus(fundBox.Text, toBox.Text, fromBox.Text);
var type = StatusReturn.GetType();
var propertyName = type.Name;
//var propertyValue = propertyName.GetValue(myObject, null);error here
}

The following code uses reflection.
StatusReturn = service.UpdateGrantBillStatus(fundBox.Text, toBox.Text, fromBox.Text);
var type = StatusReturn.GetType();
var pi = type.GetProperty("sentBatchTotal");
if (pi != null) {
var propertyValue = pi.GetValue(StatusReturn, null);
}
But can't you just the webservice-method return-type instead of object? Than you can just read the property directly.
Something like:
WhatEverTypeYourServiceReturns StatusReturn = service.UpdateGrantBillStatus(fundBox.Text, toBox.Text, fromBox.Text);
string sentBatchTotal = StatusReturn.sentBatchTotal;

Don't declare your StatusReturn variable as an object type first.
//Object StatusReturn = new Object();
var StatusReturn = service.UpdateGrantBillStatus(fundBox.Text, toBox.Text, fromBox.Text);
if (StatusReturn.Count() > 0)
{
var fixedAsset = StatusReturn[0];
}

dynamic d = service.UpdateGrantBillStatus(fundBox.Text, toBox.Text, fromBox.Text);
string result = (string)d[0].sentBatchTotal;

Related

Create Class Dynamically During Runtime

Hi im trying to create a class dending on data gathered from a user input. Once its chosen id like to the create the field names and the data types based on that and fill that class with data from document effectively creating a list of that Class.
Eg I create a class called Class1 and give it 3 Properties : ID , Name , Weight and define there types as int , string , int
Then I want to fill it with data Eg : (Example in json to show structure)
ID:{
1,
2,
3
},
Name:{
A,
B,
c
},
Weight:{
10,
20,
30
}
Ive looked into Reflection and codeDom which both enable for me to make the Class but i cannot work out how to write to that new classes properties.
Code for codeDom Version:
string className = "BlogPost";
var props = new Dictionary<string, Type>() {
{ "Title", typeof(string) },
{ "Text", typeof(string) },
{ "Tags", typeof(string[]) }
};
createType(className, props);
I Create The Properties and their Types
static void createType(string name, IDictionary<string, Type> props)
{
var csc = new CSharpCodeProvider(new Dictionary<string, string>() { { "CompilerVersion", "v4.0" } });
var parameters = new CompilerParameters(new[] { "mscorlib.dll", "System.Core.dll" }, "Test.Dynamic.dll", false);
parameters.GenerateExecutable = false;
var compileUnit = new CodeCompileUnit();
var ns = new CodeNamespace("Test.Dynamic");
compileUnit.Namespaces.Add(ns);
ns.Imports.Add(new CodeNamespaceImport("System"));
var classType = new CodeTypeDeclaration(name);
classType.Attributes = MemberAttributes.Public;
ns.Types.Add(classType);
foreach (var prop in props)
{
var fieldName = "_" + prop.Key;
var field = new CodeMemberField(prop.Value, fieldName);
classType.Members.Add(field);
var property = new CodeMemberProperty();
property.Attributes = MemberAttributes.Public | MemberAttributes.Final;
property.Type = new CodeTypeReference(prop.Value);
property.Name = prop.Key;
property.GetStatements.Add(new CodeMethodReturnStatement(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), fieldName)));
property.SetStatements.Add(new CodeAssignStatement(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), fieldName), new CodePropertySetValueReferenceExpression()));
classType.Members.Add(property);
}
var results = csc.CompileAssemblyFromDom(parameters, compileUnit);
results.Errors.Cast<CompilerError>().ToList().ForEach(error => Console.WriteLine(error.ErrorText));
}
This is just code i found elsewhere but if this where the code i wanted id do something like
var a = new List<BlogPost>()
and then
a."Property1" = "Title 1"
Hope this is informative
You could use reflection.
Main method to create desired object and populate its properties:
public object GenerateObject(string fullyQualifiedClassName,
Dictionary<string, object> nameToValueMap)
{
var actualObject = GetInstance(fullyQualifiedClassName);
if (actualObject == null)
return actualObject;
foreach (var prop in nameToValueMap)
{
SetPropValue(actualObject, prop.Key, prop.Value);
}
return actualObject;
}
Method to create instance of the desired class, based on fully qualified class name:
public object GetInstance(string fullyQualifiedName)
{
Type type = Type.GetType(fullyQualifiedName);
if (type != null)
return Activator.CreateInstance(type);
foreach (var asm in AppDomain.CurrentDomain.GetAssemblies())
{
type = asm.GetType(fullyQualifiedName);
if (type != null)
return Activator.CreateInstance(type);
}
return null;
}
And last but not least, method to set property's value:
public bool SetPropValue<T>(T obj, string propName, object val)
{
if (string.IsNullOrEmpty(propName)) return false;
var prop = obj?.GetType()
.GetProperties()?
.FirstOrDefault(m => m.Name == propName);
if (prop != null)
{
prop.SetValue(obj, val);
return true;
}
return false;
}
Why not use dynamic object using expandoObject?
something like:
dynamic blogPost = new System.Dynamic.ExpandoObject();
blogPost.Tile = "Mary Water";
blogPost.Text= "your text here";

Roslyn (Lambda) Expression Bodied Property Syntax

I wrote a function to convert LocalDeclaration's to Global Resources. Right now I'm replacing with each definition with a property, but I want to replace it with a property using the new syntax =>
public PropertyDeclarationSyntax ConvertToResourceProperty(string resouceClassIdentifier, string fieldName, string resourceKey, CSharpSyntaxNode field)
{
var stringType = SyntaxFactory.ParseTypeName("string");
var resourceReturnIdentifier = SyntaxFactory.IdentifierName(resouceClassIdentifier + "." + resourceKey);
var returnResourceStatement = SyntaxFactory.ReturnStatement(resourceReturnIdentifier).NormalizeWhitespace();
var getRescourceBlock = SyntaxFactory.Block(returnResourceStatement);
var getAccessor = SyntaxFactory.AccessorDeclaration(SyntaxKind.GetAccessorDeclaration, getRescourceBlock).WithAdditionalAnnotations(Formatter.Annotation, Simplifier.Annotation);
var propertyDeclaration = SyntaxFactory.PropertyDeclaration(stringType, fieldName).AddModifiers(SyntaxFactory.Token(SyntaxKind.PublicKeyword), SyntaxFactory.Token(SyntaxKind.StaticKeyword)).NormalizeWhitespace();
propertyDeclaration = propertyDeclaration.AddAccessorListAccessors(getAccessor).WithAdditionalAnnotations(Formatter.Annotation);
SyntaxTrivia[] leadingTrivia = field.GetLeadingTrivia().ToArray() ?? new[] { SyntaxFactory.Whitespace("\t") };
return propertyDeclaration.WithTrailingTrivia(SyntaxFactory.Whitespace("\r\n"))
.WithLeadingTrivia(leadingTrivia)
.WithAdditionalAnnotations(Simplifier.Annotation);
}
This code create a property like so:
public static string LocalResourceName
{
get{ return Resources.LocalResourceName; }
}
I would like it to make the property like so:
public static string LocalResourceName =>Resources.LocalResourceName;
I'm not too sure what will create an expression bodied property from the syntaxfactory? Can anyone point me to the right method?
After scouring the internet I've found a way to do it. Why is there no documentation for roslyn?
public PropertyDeclarationSyntax ConvertToResourceProperty(string resouceClassIdentifier, string fieldName, string resourceKey, CSharpSyntaxNode field)
{
var stringType = SyntaxFactory.ParseTypeName("string");
var resourceClassName = SyntaxFactory.IdentifierName(resouceClassIdentifier);
var resourceKeyName = SyntaxFactory.IdentifierName(resourceKey);
var memberaccess = SyntaxFactory.MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, resourceClassName, resourceKeyName);
var propertyLambda = SyntaxFactory.ArrowExpressionClause(memberaccess);
var propertyDeclaration = SyntaxFactory.PropertyDeclaration(new SyntaxList<AttributeListSyntax>(), new SyntaxTokenList(),
stringType, null, SyntaxFactory.Identifier(fieldName), null,
propertyLambda, null, SyntaxFactory.Token(SyntaxKind.SemicolonToken))
.AddModifiers(SyntaxFactory.Token(SyntaxKind.PublicKeyword),
SyntaxFactory.Token(SyntaxKind.StaticKeyword)).WithAdditionalAnnotations(Formatter.Annotation).NormalizeWhitespace();
return propertyDeclaration.WithTrailingTrivia(SyntaxFactory.ElasticCarriageReturnLineFeed)
.WithLeadingTrivia(field.GetLeadingTrivia().ToArray())
.WithAdditionalAnnotations(Simplifier.Annotation);
}

Get Object from its name

I have an object:
MyObject obj = new MyObject();
obj.X = "Hello";
obj.Y = "World";
Someone passes me a string:
string myString = "obj.X";
I want to get the value referenced to myString, like this:
var result = <Some Magic Expression>(myString); // "Hello"
Is it possible through reflection?
You can't exactly replicate this behaviour, because names of local variables aren't saved in the method's metadata. However, if you keep a dictionary of objects, you can address the object by its key:
public static object GetProperty(IDictionary<string, object> dict, string path)
{
string[] split = path.Split('.');
object obj = dict[split[0]];
var type = obj.GetType();
return type.InvokeMember(split[1], BindingFlags.Public | BindingFlags.Instance | BindingFlags.GetField | BindingFlags.GetProperty, null, obj, null);
}
 
var dict = new Dictionary<string, object>();
var cl = new MyClass();
dict["obj"] = cl;
cl.X = "1";
cl.Y = "2";
Console.WriteLine(GetProperty(dict, "obj.X"));
Console.WriteLine(GetProperty(dict, "obj.Y"));
This can handle accessing fields and properties in the format "name.property". Doesn't work for dynamic objects.

Using a PropertyGrid to input method parameters

I'd like to use a PropertyGrid to input method parameters.
I have some application that will dynamically load user's DLLs and invoke methods with specific signature (a known return type).
I'd like to present the user the option to input the arguments to the called method easily with a PropertyGrid control.
Problem is -- PropertyGrid works on an Object, and not on a method.
I'd like to somehow "transform" the method at runtime into an object with properties reflecting its arguments, passing the input values to the method when invoking it.
Offcourse i'd like to have type validation, etc (if provided by the PropertyGrid, dont remember right now).
Is there any easy solution for this?
Thanks!
Well here is what I've written yesterday.
It is meant to be run in LinqPad, which is an awesome free tool to test linq queries or code snippets. (With an inexpensive upgrade to get intellisense)
The code should tell you how to deal with different kind of parameters (ref, out) and whether you are calling an instance method or not. (flip the comments in Main to test an instance method)
In LinqPad, you can use the Dump() extension method to let it show your objects in the results window. this is handy to see what is actually happening.
So, if you want to know how to dynamically construct a type and invoke it, this should get you started:
EDIT: I totally forgot to mention, that you do need to add these 2 namespaces to the query. You do that by hitting F4->additional namespace imports and adding these 2:
System.CodeDom.Compiler
System.CodeDom
public static String TestMethod1(int a, ref int X, out string t)
{
a += X;
X = a * 2;
t = "...>" + (X + a);
return a.ToString() + "...";
}
public class TestClass
{
public int SomeMethod(int a, DateTime? xyz)
{
if(xyz != null)
a+= xyz.GetValueOrDefault().Day;
return 12 + a;
}
}
void Main()
{
var sb = new StringBuilder();
var methodInfo = typeof(UserQuery).GetMethod("TestMethod1");
dynamic instance = CreateWrapper(methodInfo, sb);
instance.a = 11;
instance.X = 2;
instance.CallMethod();
/*
var methodInfo = typeof(TestClass).GetMethod("SomeMethod");
dynamic instance = CreateWrapper(methodInfo, sb);
instance.a = 11;
instance.xyz = new DateTime(2010, 1, 2);
instance.CallMethod(new TestClass());
*/
((Object)instance).Dump();
sb.ToString().Dump();
}
static object CreateWrapper(MethodInfo methodInfo, StringBuilder sb)
{
// pick either C#, VB or another language that can handle generics
var codeDom = CodeDomProvider.CreateProvider("C#");
var unit = new CodeCompileUnit();
var codeNameSpace = new CodeNamespace();
codeNameSpace.Name = "YourNamespace";
var wrapperType = AddWrapperType(codeDom, codeNameSpace, methodInfo, "WrapperType", "MethodResultValue");
unit.Namespaces.Add(codeNameSpace);
// this is only needed so that LinqPad can dump the code
codeDom.GenerateCodeFromNamespace(codeNameSpace, new StringWriter(sb), new CodeGeneratorOptions());
// put the temp assembly in LinqPad's temp folder
var outputFileName = Path.Combine(Path.GetDirectoryName(new Uri(typeof(UserQuery).Assembly.CodeBase).AbsolutePath),
Guid.NewGuid() + ".dll");
var results = codeDom.CompileAssemblyFromDom(new CompilerParameters(new[]{new Uri(methodInfo.DeclaringType.Assembly.CodeBase).AbsolutePath,
new Uri(typeof(UserQuery).Assembly.CodeBase).AbsolutePath,
new Uri(typeof(UserQuery).BaseType.Assembly.CodeBase).AbsolutePath}.Distinct().ToArray(),
outputFileName),
unit);
results.Errors.Dump();
new Uri(results.CompiledAssembly.CodeBase).AbsolutePath.Dump();
if(results.Errors.Count == 0)
{
var compiledType = results.CompiledAssembly.GetType(codeNameSpace.Name + "." + wrapperType.Name);
return Activator.CreateInstance(compiledType);
}
return null;
}
static CodeTypeDeclaration AddWrapperType(CodeDomProvider codeDom,
CodeNamespace codeNameSpace,
MethodInfo methodInfo,
string typeName,
string resultPropertyName)
{
var parameters = (from parameter in methodInfo.GetParameters()
select parameter).ToList();
var returnValue = methodInfo.ReturnType;
if(!String.IsNullOrEmpty(methodInfo.DeclaringType.Namespace))
codeNameSpace.Imports.Add(new CodeNamespaceImport(methodInfo.DeclaringType.Namespace));
var wrapperType = new CodeTypeDeclaration(typeName);
var defaultAttributes = MemberAttributes.Public | MemberAttributes.Final;
var thisRef = new CodeThisReferenceExpression();
Func<Type, Type> getRealType = t => t.IsByRef || t.IsPointer ? t.GetElementType(): t;
Func<String, String> getFieldName = parameterName => "m_" + parameterName + "_Field";
Action<ParameterInfo> addProperty = p =>
{
var realType = getRealType(p.ParameterType);
var usedName = p.Position == -1 ? resultPropertyName : p.Name;
wrapperType.Members.Add(new CodeMemberField
{
Name = getFieldName(usedName),
Type = new CodeTypeReference(realType),
Attributes= MemberAttributes.Private
});
var property = new CodeMemberProperty
{
Name = usedName,
Type = new CodeTypeReference(realType),
Attributes= defaultAttributes
};
property.GetStatements.Add(new CodeMethodReturnStatement(new CodeFieldReferenceExpression(thisRef,
getFieldName(usedName))));
property.SetStatements.Add(new CodeAssignStatement(new CodeFieldReferenceExpression(thisRef, getFieldName(usedName)),
new CodeArgumentReferenceExpression("value")));
wrapperType.Members.Add(property);
};
parameters.ForEach(addProperty);
if(methodInfo.ReturnParameter != null)
{
addProperty(methodInfo.ReturnParameter);
}
var callMethod = new CodeMemberMethod
{
Name="CallMethod",
Attributes=defaultAttributes
};
CodeMethodInvokeExpression invokeExpr;
if(!methodInfo.IsStatic)
{
callMethod.Parameters.Add(new CodeParameterDeclarationExpression(methodInfo.DeclaringType,
"instance"));
invokeExpr = new CodeMethodInvokeExpression(new CodeArgumentReferenceExpression("instance"),
methodInfo.Name);
}
else
invokeExpr = new CodeMethodInvokeExpression(new CodeTypeReferenceExpression(methodInfo.DeclaringType), methodInfo.Name);
foreach(var parameter in parameters)
{
CodeExpression fieldExpression = new CodeFieldReferenceExpression(thisRef,
getFieldName(parameter.Name));
if(parameter.ParameterType.IsByRef && !parameter.IsOut)
fieldExpression = new CodeDirectionExpression(FieldDirection.Ref, fieldExpression);
else if(parameter.IsOut)
fieldExpression = new CodeDirectionExpression(FieldDirection.Out, fieldExpression);
else if(parameter.IsIn)
fieldExpression = new CodeDirectionExpression(FieldDirection.In, fieldExpression);
invokeExpr.Parameters.Add(fieldExpression);
}
wrapperType.Members.Add(callMethod);
if(returnValue != typeof(void))
callMethod.Statements.Add(new CodeAssignStatement(new CodeFieldReferenceExpression(thisRef,
getFieldName(resultPropertyName)),
invokeExpr));
else
callMethod.Statements.Add(invokeExpr);
codeNameSpace.Types.Add(wrapperType);
return wrapperType;
}
I think you could add a new class to your project that implement the ICustomTypeDescriptor interface. And use the instance of this class as the wrapper of your method parameters.
Here is an article shows how to custom property grid display by implementing ICustomTypeDescriptor.

how to execute string path on dynamic type?

Is it possible to execute string path on dynamic type?
For example having dynamic type we can write
dynamic d = myObj;
var v = d.MyMethod(1,"text").SomeProperty.Name
Now imagine I have string path
string path = "MyMethod(1,\"text\").SomeProperty.Name";
var v = d. //How to applay string path to this type?
I have solution using extension method and reflection, you need to optimize and test for different scenario.
EDIT
Still dirty code, but supports overloaded method now. I will try to do code clean up and use regex for effective and cleaner solution
You can specify data types for parameters now to the eval method.
string epath = "GetName(System_String: ding dong, System_Int32:1).name";
MyClass cls = new MyClass();
var v = cls.Eval(epath);
Note underscore in type names. This should work without mentioning datatypes if method are not overloaded. Current restriction, you cannot use colon or comma inside string parameter value. :(
Call like var v = d.Execute(path)
public static object Eval(this object instance, string path)
{
string[] cmd = path.Split('.');
string subString = cmd[0];
object returnValue = null;
Type t = instance.GetType();
if (subString.Contains("("))
{
string[] paramString = subString.Split('(');
string[] parameters = paramString[1].Replace(")", "").Split(new Char[]{','},StringSplitOptions.RemoveEmptyEntries);
bool hasNoParams = parameters.Length == 0;
List<Type> typeArray = null;
if (hasNoParams) typeArray = new List<Type>();
foreach (string parameter in parameters)
{
if (parameter.Contains(":"))
{
if (typeArray == null) typeArray = new List<Type>();
string[] typeValue = parameter.Split(':');
Type paramType = Type.GetType(typeValue[0].Replace('_','.'));
typeArray.Add(paramType);
}
}
MethodInfo info = null;
if (typeArray == null)
info = t.GetMethod(paramString[0]);
else
info = t.GetMethod(paramString[0], typeArray.ToArray());
ParameterInfo[] pInfo = info.GetParameters();
List<object> paramList = new List<object>();
for (int i = 0; i < pInfo.Length; i++)
{
string currentParam = parameters[i];
if (currentParam.Contains(":"))
{
currentParam = currentParam.Split(':')[1];
}
ParameterInfo pram = pInfo[i];
Type pType = pram.ParameterType;
object obj = Convert.ChangeType(currentParam, pType);
paramList.Add(obj);
}
if (info == null) returnValue = null;
else
returnValue = info.Invoke(instance, paramList.ToArray());
}
else
{
PropertyInfo pi = t.GetProperty(subString);
if (pi == null) returnValue = null;
else
returnValue = pi.GetValue(instance, null);
}
if (returnValue == null || cmd.Length == 1)
return returnValue;
else
{
returnValue = returnValue.Eval(path.Replace(cmd[0] + ".", ""));
}
return returnValue;
}
It seems you would need an eval function and C# has none but if this third party C# eval implementation can handle dynamic it might solve your problem.

Categories