So I have the following:
public class Singleton
{
private Singleton(){}
public static readonly Singleton instance = new Singleton();
public string DoSomething(){ ... }
public string DoSomethingElse(){ ... }
}
Using reflection how can I invoke the DoSomething Method?
Reason I ask is because I store the method names in XML and dynamically create the UI. For example I'm dynamically creating a button and telling it what method to call via reflection when the button is clicked. In some cases it would be DoSomething or in others it would be DoSomethingElse.
Untested, but should work...
string methodName = "DoSomething"; // e.g. read from XML
MethodInfo method = typeof(Singleton).GetMethod(methodName);
FieldInfo field = typeof(Singleton).GetField("instance",
BindingFlags.Static | BindingFlags.Public);
object instance = field.GetValue(null);
method.Invoke(instance, Type.EmptyTypes);
Great job. Thanks.
Here's the same approach with slight modification for cases that one can't have a reference to the remote assembly. We just need to know basic things such as the class fullname (i.e namespace.classname and the path to the remote assembly).
static void Main(string[] args)
{
Assembly asm = null;
string assemblyPath = #"C:\works\...\StaticMembers.dll"
string classFullname = "StaticMembers.MySingleton";
string doSomethingMethodName = "DoSomething";
string doSomethingElseMethodName = "DoSomethingElse";
asm = Assembly.LoadFrom(assemblyPath);
if (asm == null)
throw new FileNotFoundException();
Type[] types = asm.GetTypes();
Type theSingletonType = null;
foreach(Type ty in types)
{
if (ty.FullName.Equals(classFullname))
{
theSingletonType = ty;
break;
}
}
if (theSingletonType == null)
{
Console.WriteLine("Type was not found!");
return;
}
MethodInfo doSomethingMethodInfo =
theSingletonType.GetMethod(doSomethingMethodName );
FieldInfo field = theSingletonType.GetField("instance",
BindingFlags.Static | BindingFlags.Public);
object instance = field.GetValue(null);
string msg = (string)doSomethingMethodInfo.Invoke(instance, Type.EmptyTypes);
Console.WriteLine(msg);
MethodInfo somethingElse = theSingletonType.GetMethod(
doSomethingElseMethodName );
msg = (string)doSomethingElse.Invoke(instance, Type.EmptyTypes);
Console.WriteLine(msg);}
Related
I hope the code snipped illustrates my issue.
I need to Invoke the CallEvent method like it is in the out-commented line.
I have no access to the ThirdParty or AnotherThirdParty class.
This is as far as I come:
public class ThirdParty
{
private struct MsgType
{ }
private static void AnotherFunc(MsgType msg)
{ }
}
public class AnotherThirdParty
{
public static void CallEvent<T>(Func<int, Action<T>> action, T arg)
{ }
}
public class MyClass
{
public static void Main()
{
Type MsgType = typeof(ThirdParty).GetNestedType(
"MsgType", BindingFlags.Instance | BindingFlags.NonPublic);
object msg = Activator.CreateInstance(MsgType);
MethodInfo CallEvent = typeof(AnotherThirdParty).GetMethod("CallEvent");
CallEvent = CallEvent.MakeGenericMethod(MsgType);
MethodInfo AnotherFunc = typeof(ThirdParty).GetMethod(
"AnotherFunc", BindingFlags.Static | BindingFlags.NonPublic);
CallEvent.Invoke(null, new object[] {???, msg});
//CallEvent<MsgType>((int x) => new Action<MsgType>(AnotherFunc), msg);
// I can't get my head around how to solve this (Action<msgtype>)
}
}
I also tried:
CallEvent.Invoke(null, new object[]
{
new Func<int, Action<object>>((int x) =>
new Action<object>((object y) =>
AnotherFunc.Invoke(null, new object[] { y }))),
msg
});
I get the following Exception:
System.ArgumentException: Object of type
'System.Func2[System.Int32,System.Action1[System.Object]]' cannot be
converted to type
'System.Func2[System.Int32,System.Action1[ThirdParty+MsgType]].
How should I proceed?
public class ThirdParty
{
private struct MsgType { }
private static void AnotherFunc(MsgType msg)
{
// Inserted to demonstrate getting here
Console.WriteLine($"HEY: {msg}");
}
}
public class AnotherThirdParty
{
public static void CallEvent<T>(Func<int, Action<T>> action, T arg)
{
// Inserted to demonstrate calling the func and then
// the action
action(12)(arg);
}
}
public static void Main()
{
var msgTypeType =
typeof(ThirdParty).GetNestedType("MsgType", BindingFlags.NonPublic);
// This is the message type we're passing (presumably you'll do more with it)
var ourMsgTypeArg = Activator.CreateInstance(msgTypeType);
// Get the reference to the CallEvent method
var callEventMethod =
typeof(AnotherThirdParty).GetMethod("CallEvent", BindingFlags.Public | BindingFlags.Static)
.MakeGenericMethod(msgTypeType);
// Get the reference to the AnotherFunc method
var anotherFunc =
typeof(ThirdParty).GetMethod("AnotherFunc", BindingFlags.NonPublic | BindingFlags.Static);
// Build the func to pass along to CallEvent
var func = CreateFunc(msgTypeType, anotherFunc);
// Call the CallEvent<MsgType> method.
callEventMethod.Invoke(null, new object[] {
func,
ourMsgTypeArg
});
}
private static Delegate CreateFunc(Type msgType, MethodInfo anotherFunc)
{
// The func takes an int
var intArg = Expression.Parameter(typeof(int));
// The action takes a msgType
var msgTypeArg = Expression.Parameter(msgType);
// Represent the call out to "AnotherFunc"
var call = Expression.Call(null, anotherFunc, msgTypeArg);
// Build the action to just make the call to "AnotherFunc"
var action = Expression.Lambda(call, msgTypeArg);
// Build the func to just return the action
var func = Expression.Lambda(action, intArg);
// Compile the chain and send it out
return func.Compile();
}
This code functions as you've requested and prints the following:
HEY: UserQuery+ThirdParty+MsgType
This seems to run:
MethodInfo miCreateDelegate = typeof(MethodInfo).GetMethod("CreateDelegate", new[] { typeof(Type), typeof(Object) });
var ActionType = typeof(Action<>).MakeGenericType(MsgType);
var lambdabody = Expression.Convert(Expression.Call(Expression.Constant(AnotherFunc), miCreateDelegate, new[] { Expression.Constant(ActionType), Expression.Constant(null) }), ActionType);
var intparm = Expression.Parameter(typeof(int));
var lambda = Expression.Lambda(lambdabody, intparm);
CallEvent.Invoke(null, new object[] {
lambda.Compile(),
msg
});
A more complete answer is how did I generate this? I used LINQPad to compile a simpler, similar expression substituting string for MsgType into an Expression:
public static void afunc(string x) { }
Expression<Func<int, Action<string>>> lambda = (int x) => new Action<string>(afunc);
Then I used the LINQPad Dump() function to output the expression tree.
lambda.Dump();
Then some spelunking in MSDN Expression documentation gave me the right static methods to create the pieces. I already knew how to instantiate generic types from an extension method for LINQPad that creates anonymous types on the fly to extend Dump() to exclude fields from anonymous objects, and I knew how to create lambdas from an extension method that extends LINQ with a proper SQL-translatable Left and Right Join operation.
Use Delegate.CreateDelegate method to construct an Action<MsgType> object. Construct Func<int,Action<T>> using Expression.Lambda<>:
var actionType = typeof(Action<>).MakeGenericType(MsgType);
var funcType = typeof(Func<,>).MakeGenericType(typeof(int), actionType);
var p1 = Expression.Parameter(typeof(int));
var p2 = Expression.Parameter(actionType);
var delegate = Expression.Constant(Delegate.CreateDelegate(actionType, AnotherFunc), funcType);
var lambda = Expression.Lambda(delegate, p1, p2);
CallEvent.Invoke(null, new object[] {
lambda.Compile()
, msg
});
This will work and will print an A but the function factory is a mistery to me so I just returned the created delegate. And this is compatible with .net standard 1.1
static void Main(string[] args)
{
Type MsgType = typeof(ThirdParty).GetNestedType(
"MsgType", BindingFlags.Instance | BindingFlags.NonPublic);
object msg = Activator.CreateInstance(MsgType);
MethodInfo CallEvent = typeof(AnotherThirdParty).GetMethod("CallEvent");
CallEvent = CallEvent.MakeGenericMethod(MsgType);
MethodInfo AnotherFunc = typeof(ThirdParty).GetMethod(
"AnotherFunc", BindingFlags.Static | BindingFlags.NonPublic);
var actionType = typeof(Action<>).MakeGenericType(MsgType);
var actionDelegate = AnotherFunc.CreateDelegate(actionType);
var param = Expression.Parameter(typeof(int));
var funcDelegate = Expression.Lambda(Expression.Constant(actionDelegate),param).Compile();
CallEvent.Invoke(null, new []{ funcDelegate, msg });
Console.ReadLine();
}
public class ThirdParty
{
private struct MsgType
{ }
private static void AnotherFunc(MsgType msg)
{
Console.WriteLine("A");
}
}
public class AnotherThirdParty
{
public static void CallEvent<T>(Func<int, Action<T>> action, T arg)
{
action(1)(arg);
}
}
I have some not useful object with, let's say, 30 properties. Half of them are useful for me, so I want to create new useful object with only properties I need so the rest doesn't take space in object visualizers. I don't want to define new class and type them down. I want something like
var list = new List<SomeType> { usefulProp1, usefulProp2, ... };
var usefulObject = new NewItem(notUsefulObject, list);
where SomeType is not string (list doesn't contain property names).
if its something permanent then doing it properly would be to create a class that inherits from your base class and populate only the properties that you need.
public UsefulObject : NotUsefulObject
{
public int MyProperty
{
get
{
return base.MyProperty; // this is arbitrary you can do it however you want.
}
set
{
MyProperty = value;
}
}
}
Then you can use your reuse your useful object however you want.
var list = new List<UsefulObject>();
var list = new List<Expression<Func<object>>> { () => notUsefulObject.usefulProp1, () => notUsefulObject.usefulProp2... };
var nm = new AssemblyName("MyDynamicAssembly");
TypeBuilder tb = Thread.GetDomain().DefineDynamicAssembly(nm, AssemblyBuilderAccess.RunAndSave).DefineDynamicModule(nm.Name, nm.Name + ".dll").DefineType("NewItem", TypeAttributes.Public);
const MethodAttributes GetSetAttr = MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig;
foreach (Expression b in list.Select(x => x.Body))
{
MemberInfo mi = ((MemberExpression)b).Member;
Type t = b.Type;
FieldBuilder fb = tb.DefineField(mi.Name.ToLower(), t, FieldAttributes.Private);
PropertyBuilder pb = tb.DefineProperty(mi.Name, PropertyAttributes.HasDefault, t, null);
MethodBuilder getBld = tb.DefineMethod("get_" + mi.Name, GetSetAttr, t, Type.EmptyTypes);
ILGenerator getGen = getBld.GetILGenerator();
getGen.Emit(OpCodes.Ldarg_0);
getGen.Emit(OpCodes.Ldfld, fb);
getGen.Emit(OpCodes.Ret);
MethodBuilder setBld = tb.DefineMethod("set_" + mi.Name, GetSetAttr, null, new[] { t });
ILGenerator setGen = setBld.GetILGenerator();
setGen.Emit(OpCodes.Ldarg_0);
setGen.Emit(OpCodes.Ldarg_1);
setGen.Emit(OpCodes.Stfld, fb);
setGen.Emit(OpCodes.Ret);
pb.SetGetMethod(getBld);
pb.SetSetMethod(setBld);
}
object usefulObject = Activator.CreateInstance(tb.CreateType());
Because you said this problem was specific to object visualizers, the best solution is to make a Debugger Type Proxy for the class which can be used as a proxy.
[DebuggerTypeProxy(typeof(SomeTypeDebugView))]
public class SomeType
{
public string Foo { get; set; }
public string Bar { get; set; }
internal class SomeTypeDebugView
{
private SomeType _someType;
public SomeTypeDebugView(SomeType someType)
{
_someType = someType;
}
public string Foo { get { return _someType.Foo; } }
}
}
class Program
{
static void Main()
{
var someType = new SomeType();
someType.Foo = "foo";
someType.Bar = "bar";
Debugger.Break();
}
}
This will cause Bar to be hidden in the debug visualizer by default unless you choose to view the "Raw Type"
If you are looking for a more one time debugging type you can use annonamous types to create the new type you need in the watch window, for example his is new {Foo = someType.Foo, Bar = someType.Bar }
This also could be used with LINQ to perform the select over a IEnumerable, here for example is someTypeList.Select(x=> new {Foo = x.Foo, Bar = x.Bar})
I have an API that accepts xml messages. Suppose I have obtained object Thing from this API which looks like this:
<Thing shape="circle" color="red"/>
and is mapped to:
[XmlRoot("Thing")]
public class Thing {
[XmlAttribute("shape")]
public string Shape { get; set; }
[XmlAttribute("color")]
public string Color { get; set; }
}
Now I want to update this object so that eg. color is green. The API requires me to send it in the following format:
<Thing color="green" o_color="red"/>
Is there a way to generate o_* properties on the fly? So that when they are set outside of constructor their old value is stored in some generated property that XmlSerializer maps to o_? I know I could simply create those properties manually, but for bigger objects its a tedious work. I've tried to do that with Castle's Dynamic Proxy, which I already use in the project, but it seems it just can't add properties like that (or I haven't found out how to do that)
You have a couple of issues that need to be solved here. The fist is you must maintain state in each of your serialized objects that will tell you if a property has been altered, and what its original value is. Then your second problem is you need to build a new object dynamically based on this maintained state. With the implementation I provide you will need to determine if it meets your performance needs as it is doing a lot of Reflection. You will also have to weight its complexity with other solutions. The advantage I see with this solution is not having to maintain two very similar object.
!! Important !! This code is not Production ready. I have left a few todos in the code that need to be flushed out.
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Reflection.Emit;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Serialization;
namespace ConsoleApplication1
{
public abstract class PropertyStateTracker
{
private class PropertyDetails
{
public object CurrentValue { get; set; }
public bool HasChanged { get; set; }
public object OriginalValue { get; set; }
}
private readonly Dictionary<string, PropertyDetails> propertyState =
new Dictionary<string, PropertyDetails>();
protected TProperty Get<TProperty>(Expression<Func<TProperty>> propertySelector)
{
string name = GetNameFromExpression(propertySelector);
PropertyDetails data;
if (!propertyState.TryGetValue(name, out data))
{
Set(propertySelector, default(TProperty));
return default(TProperty);
}
return (TProperty)data.CurrentValue;
}
protected void Set<TProperty>(Expression<Func<TProperty>> propertySelector, TProperty value)
{
string name = GetNameFromExpression(propertySelector);
PropertyDetails data;
if (!propertyState.TryGetValue(name, out data))
{
data = new PropertyDetails() { OriginalValue = value, CurrentValue = value, HasChanged = false };
propertyState[name] = data;
}
else
{
data.CurrentValue = value;
data.HasChanged = true;
}
}
[XmlIgnore]
public IEnumerable<string> ChangedProperties
{
get
{
foreach (var property in propertyState)
{
if (property.Value.HasChanged)
{
yield return property.Key;
}
}
}
}
public bool HasChanged<TProperty>(Expression<Func<TProperty>> propertySelector)
{
string name = GetNameFromExpression(propertySelector);
return HasChanged(name);
}
public bool HasChanged(string propertyName)
{
EnsurePropertyExists(propertyName);
PropertyDetails data;
if (!propertyState.TryGetValue(propertyName, out data))
{
return false;
}
return data.HasChanged;
}
public TProperty GetOriginalValue<TProperty>(Expression<Func<TProperty>> propertySelector)
{
string name = GetNameFromExpression(propertySelector);
return (TProperty)GetOriginalValue(name);
}
public object GetOriginalValue(string propertyName)
{
EnsurePropertyExists(propertyName);
PropertyDetails data;
if (!propertyState.TryGetValue(propertyName, out data))
{
return GetDefaultValue(GetPropertyInfo(propertyName).PropertyType);
}
return data.OriginalValue;
}
public TProperty GetCurrentValue<TProperty>(Expression<Func<TProperty>> propertySelector)
{
string name = GetNameFromExpression(propertySelector);
return (TProperty)GetCurrentValue(name);
}
public object GetCurrentValue(string propertyName)
{
EnsurePropertyExists(propertyName);
PropertyDetails data;
if (!propertyState.TryGetValue(propertyName, out data))
{
return GetDefaultValue(GetPropertyInfo(propertyName).PropertyType);
}
return data.CurrentValue;
}
public void Reset()
{
foreach (var property in propertyState)
{
property.Value.OriginalValue = property.Value.CurrentValue;
property.Value.HasChanged = false;
}
}
private void EnsurePropertyExists(string propertyName)
{
PropertyInfo property = GetPropertyInfo(propertyName);
if (property == null)
{
throw new ArgumentException(string.Format("A property named '{0}' was not found for type '{1}'",
propertyName, this.GetType().Name));
}
}
private PropertyInfo GetPropertyInfo(string propertyName)
{
Type type = this.GetType();
var property = type.GetProperty(propertyName, BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase);
return property;
}
private static object GetDefaultValue(Type t)
{
if (t.IsValueType)
return Activator.CreateInstance(t);
return null;
}
private static string GetNameFromExpression<TMember>(Expression<Func<TMember>> lambda)
{
// check to make sure a non-null lambda was provided.
if (lambda == null)
{
throw new ArgumentNullException("lambda");
}
Expression expression = lambda.Body;
// is the expression's body a unary expression.
var unaryExpression = expression as UnaryExpression;
if (unaryExpression != null && unaryExpression.NodeType == ExpressionType.Convert)
{
expression = unaryExpression.Operand;
}
// is the expression's body a parameter expression.
var parameterExpression = expression as ParameterExpression;
if (parameterExpression != null)
{
return parameterExpression.Name;
}
// is the expression's body a member expression.
var memberExpression = expression as MemberExpression;
if (memberExpression != null)
{
return memberExpression.Member.Name;
}
// is the expression's body a method call expression.
var methodCallExpression = expression as MethodCallExpression;
if (methodCallExpression != null)
{
return methodCallExpression.Method.Name;
}
// unable to derive name. throw an exception.
throw new Exception(
string.Format("Failed to derive name from expression '{0}'",
expression));
}
}
[XmlRoot("Thing")]
public class Thing : PropertyStateTracker
{
[XmlAttribute("shape")]
public string Shape
{
get { return Get(() => Shape); }
set { Set(() => Shape, value); }
}
[XmlAttribute("color")]
public string Color
{
get { return Get(() => Color); }
set { Set(() => Color, value); }
}
}
class Program
{
private static long count = 0;
static void Main(string[] args)
{
Thing thingInstance;
System.Xml.Serialization.XmlSerializer serializer = new XmlSerializer(typeof(Thing));
string rawData = "<Thing shape=\"circle\" color=\"red\"/>";
using (System.IO.StringReader reader = new System.IO.StringReader(rawData))
{
thingInstance = (Thing)serializer.Deserialize(reader);
}
thingInstance.Color = "green";
// these two variables should be reused every time a new proxy is created. you dont want too many dynamic assemblies.
var assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName("DynamicThingAssembly"), AssemblyBuilderAccess.Run);
var moduleBuilder = assemblyBuilder.DefineDynamicModule("DynamicThingModule");
// TODO: need to figure out a way to reuse the proxyTypes being generated or you will have a
// memory leak. the type is unique based on the properties that have been modified, so you should be able to use
// the state stored inside of thingInstance to figure this out. I leave this up to you to implement.
Type proxyType = CreateProxy(moduleBuilder, thingInstance);
var proxy = Activator.CreateInstance(proxyType);
foreach (var propertyName in thingInstance.ChangedProperties)
{
proxyType.GetProperty(propertyName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.IgnoreCase)
.SetValue(proxy, thingInstance.GetCurrentValue(propertyName));
proxyType.GetProperty("O_" + propertyName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.IgnoreCase)
.SetValue(proxy, thingInstance.GetOriginalValue(propertyName));
}
// Important this XmlSerializer should be cached, otherwise you will have a memory leak in your program.
System.Xml.Serialization.XmlSerializer serializer2 = new XmlSerializer(proxyType, new XmlRootAttribute("Thing"));
StringBuilder sb = new StringBuilder();
using (System.IO.StringWriter writer = new System.IO.StringWriter(sb))
{
serializer2.Serialize(writer, proxy);
}
Console.Write(sb.ToString());
}
private static Type CreateProxy(ModuleBuilder moduleBuilder, Thing thing)
{
var typeName = "DynamicType" + System.Threading.Interlocked.Increment(ref count).ToString("X5");
TypeBuilder typeBuilder = moduleBuilder.DefineType(typeName, TypeAttributes.Public);
Type t = typeof(Thing);
foreach (var propertyName in thing.ChangedProperties)
{
var propertyInfo = t.GetProperty(propertyName,
BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase);
CreateProperty(typeBuilder, propertyInfo.Name, propertyInfo.PropertyType);
CreateProperty(typeBuilder, "O_" + propertyInfo.Name, propertyInfo.PropertyType);
}
return typeBuilder.CreateType();
}
private static void CreateProperty(TypeBuilder typeBuilder, string propertyName, Type propertyType)
{
var fieldBuilder = typeBuilder.DefineField("_" + propertyName, propertyType, FieldAttributes.Private);
// The last argument of DefineProperty is null, because the
// property has no parameters. (If you don't specify null, you must
// specify an array of Type objects. For a parameterless property,
// use an array with no elements: new Type[] {})
var propertyBuilder = typeBuilder.DefineProperty(
propertyName, PropertyAttributes.HasDefault, propertyType, null);
var attributeConstructor = typeof(XmlAttributeAttribute).GetConstructor(new Type[] { typeof(string) });
propertyBuilder.SetCustomAttribute(
new CustomAttributeBuilder(
attributeConstructor,
new object[] { propertyName.ToLower() }));
// The property set and property get methods require a special
// set of attributes.
MethodAttributes getSetAttr =
MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig;
MethodBuilder getPropertyMethodBuilder =
typeBuilder.DefineMethod("get_" + propertyName,
getSetAttr,
propertyType,
Type.EmptyTypes);
// Create the get methods body.
ILGenerator getPropertyMethodILGenerator = getPropertyMethodBuilder.GetILGenerator();
getPropertyMethodILGenerator.Emit(OpCodes.Ldarg_0);
getPropertyMethodILGenerator.Emit(OpCodes.Ldfld, fieldBuilder);
getPropertyMethodILGenerator.Emit(OpCodes.Ret);
// Define the "set" accessor method for CustomerName.
MethodBuilder setPropertyMethodBuilder =
typeBuilder.DefineMethod("set_" + propertyName,
getSetAttr,
null,
new Type[] { propertyType });
// Create the set methods body.
ILGenerator setPropertyMethodILGenerator = setPropertyMethodBuilder.GetILGenerator();
setPropertyMethodILGenerator.Emit(OpCodes.Ldarg_0);
setPropertyMethodILGenerator.Emit(OpCodes.Ldarg_1);
setPropertyMethodILGenerator.Emit(OpCodes.Stfld, fieldBuilder);
setPropertyMethodILGenerator.Emit(OpCodes.Ret);
// Last, we must map the two methods created above to our PropertyBuilder to
// their corresponding behaviors, "get" and "set" respectively.
propertyBuilder.SetGetMethod(getPropertyMethodBuilder);
propertyBuilder.SetSetMethod(setPropertyMethodBuilder);
}
}
}
How do you like this approach?
Thing thing;
var xs = new XmlSerializer(typeof(Thing));
thing = (Thing)xs.Deserialize(inputStream);
// Stored the old value in the property that will not be used.
thing.Shape = thing.Color;
thing.Color = "green"; // set new value
// Rename unused Shape property to o_color
var attributes = new XmlAttributes { XmlAttribute = new XmlAttributeAttribute("o_color") };
var overrides = new XmlAttributeOverrides();
overrides.Add(typeof(Thing), "Shape", attributes);
xs = new XmlSerializer(typeof(Thing), overrides);
xs.Serialize(outputStream, thing);
The Color property is used for its intended purpose: to store the new color value.
While the Shape property that is not returned in your example back, we use for the previous color value are pre-renamed it.
Of course, this is only work in this exceptional case: the data types match.
I'm attempting to create an instance of a DirectoryEntry so that I can use this to test some code that will be passed a DirectoryEntry. However, despite a lot of trying I can't find a way to instantiate a DE and initialize it's PropertyCollection.
I have the following code which was taken and modified from another answer on SO that was doing the same process but for the SearchResult object. It seems that the Add method has been completely disabled and I can't find a way to call a constructor on PropertyCollection to pass in some properties.
using System.Collections;
using System.DirectoryServices;
using System.Globalization;
using System.Reflection;
using System.Runtime.Serialization;
public static class DirectoryEntryFactory
{
const BindingFlags nonPublicInstance = BindingFlags.NonPublic | BindingFlags.Instance;
const BindingFlags publicInstance = BindingFlags.Public | BindingFlags.Instance;
public static DirectoryEntry Construct<T>(T anonInstance)
{
var e = GetUninitializedObject<DirectoryEntry>();
SetPropertiesField(e);
var dictionary = (IDictionary)e.Properties;
var type = typeof(T);
var propertyInfos = type.GetProperties(publicInstance);
foreach (var propertyInfo in propertyInfos)
{
var value = propertyInfo.GetValue(anonInstance, null);
var valueCollection = GetUninitializedObject<PropertyValueCollection>();
var innerList = GetInnerList(valueCollection);
innerList.Add(value);
var lowerKey = propertyInfo.Name.ToLower(CultureInfo.InvariantCulture);
// These both throw exceptions saying you can't add to a PropertyCollection
//(typeof(PropertyCollection)).InvokeMember("System.Collections.IDictionary.Add", nonPublicInstance | BindingFlags.InvokeMethod, null, dictionary, new object[] { propertyInfo.Name, value });
//dictionary.Add(lowerKey, propertyCollection);
}
return e;
}
private static ArrayList GetInnerList(object propertyCollection)
{
var propertyInfo = typeof(PropertyValueCollection).GetProperty("InnerList", nonPublicInstance);
return (ArrayList)propertyInfo.GetValue(propertyCollection, null);
}
private static void SetPropertiesField(DirectoryEntry e)
{
var propertiesField = typeof(DirectoryEntry).GetField("propertyCollection", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
propertiesField.SetValue(e, GetUninitializedObject<PropertyCollection>());
}
private static T GetUninitializedObject<T>()
{
return (T)FormatterServices.GetUninitializedObject(typeof(T));
}
}
usage is intended to be
DirectoryEntry e = DirectoryEntryFactory.Construct(new { attr1 = "Hello", attr2 = "World"});
I'm hoping I've missed something as I'm pretty new to using reflection in anger.
I'm not familiar with DirectoryEntry itself, but I'm a big fan of the Adapter pattern for testing. It is an annoyance that you would have to do such a thing, but the code itself is trivial and the classes can be placed in a project folder to hide them away from your main project.
For example, I have a FileInfoAdapter and DirectoryInfoAdapter that wrap those classes that touch the filesystem.
FileInfoAdapter:
public class FileInfoAdapter : IFileInfo
{
private readonly FileSystemInfo _fi;
public FileInfoAdapter(string fileName)
: this(new FileInfo(fileName))
{ }
public FileInfoAdapter(FileSystemInfo fi)
{
_fi = fi;
}
public string Name { get { return _fi.Name; } }
public string FullName { get { return _fi.FullName; } }
public bool Exists { get { return _fi.Exists; } }
}
I know in php you are able to make a call like:
$function_name = 'hello';
$function_name();
function hello() { echo 'hello'; }
Is this possible in .Net?
Yes. You can use reflection. Something like this:
Type thisType = this.GetType();
MethodInfo theMethod = thisType.GetMethod(TheCommandString);
theMethod.Invoke(this, userParameters);
With the above code, the method which is invoked must have access modifier public. If calling a non-public method, one needs to use the BindingFlags parameter, e.g. BindingFlags.NonPublic | BindingFlags.Instance:
Type thisType = this.GetType();
MethodInfo theMethod = thisType
.GetMethod(TheCommandString, BindingFlags.NonPublic | BindingFlags.Instance);
theMethod.Invoke(this, userParameters);
You can invoke methods of a class instance using reflection, doing a dynamic method invocation:
Suppose that you have a method called hello in a the actual instance (this):
string methodName = "hello";
//Get the method information using the method info class
MethodInfo mi = this.GetType().GetMethod(methodName);
//Invoke the method
// (null- no parameter for the method call
// or you can pass the array of parameters...)
mi.Invoke(this, null);
class Program
{
static void Main(string[] args)
{
Type type = typeof(MyReflectionClass);
MethodInfo method = type.GetMethod("MyMethod");
MyReflectionClass c = new MyReflectionClass();
string result = (string)method.Invoke(c, null);
Console.WriteLine(result);
}
}
public class MyReflectionClass
{
public string MyMethod()
{
return DateTime.Now.ToString();
}
}
This code works in my console .Net application
class Program
{
static void Main(string[] args)
{
string method = args[0]; // get name method
CallMethod(method);
}
public static void CallMethod(string method)
{
try
{
Type type = typeof(Program);
MethodInfo methodInfo = type.GetMethod(method);
methodInfo.Invoke(method, null);
}
catch(Exception ex)
{
Console.WriteLine("Error: " + ex.Message);
Console.ReadKey();
}
}
public static void Hello()
{
string a = "hello world!";
Console.WriteLine(a);
Console.ReadKey();
}
}
A slight tangent -- if you want to parse and evaluate an entire expression string which contains (nested!) functions, consider NCalc (http://ncalc.codeplex.com/ and nuget)
Ex. slightly modified from the project documentation:
// the expression to evaluate, e.g. from user input (like a calculator program, hint hint college students)
var exprStr = "10 + MyFunction(3, 6)";
Expression e = new Expression(exprString);
// tell it how to handle your custom function
e.EvaluateFunction += delegate(string name, FunctionArgs args) {
if (name == "MyFunction")
args.Result = (int)args.Parameters[0].Evaluate() + (int)args.Parameters[1].Evaluate();
};
// confirm it worked
Debug.Assert(19 == e.Evaluate());
And within the EvaluateFunction delegate you would call your existing function.