With
public abstract class CompositionPlugin { ... }
and
public class MyCompositionPlugin : CompositionPlugin { ... }
I want to check if an object's type is equal to a given type:
public class Framework {
public IList<CompositionPlugin> CompositionPlugins = new List<CompositionPlugin>();
public CompositionPlugin GetCompositionPlugin(Type ofType)
{
foreach (CompositionPlugin plugin in CompositionPlugins)
{
if (plugin.GetType().Equals(ofType))
return plugin;
}
throw new ArgumentException("A composition plugin of type " + ofType.FullName + " could not be found");
}
}
Where the method is called like this:
Framework framework = new Framework();
// Adding the plugin to the framework is done by loading an assembly and
// checking if it contains plugin-compatible classes (i.e. subclasses
// of <CompositionPlugin>)
framework.RegisterAssembly("E:\\Projects\\Framework\\MyPlugin\\bin\\Debug\\MyPlugin.dll");
CompositionPlugin plugin = framework.GetCompositionPlugin(typeof(MyCompositionPlugin));
Yet, when testing, this check always fails, even though I most definitely have that type of object in the list that I request.
In my understanding, it should return the first instance of MyCompositionPlugin that is found inside the CompositionPlugins-List.
Is my type check wrong? Why? How is it done correctly?
You want to use IsAssignableFrom on your Type:
if (ofType.IsAssignableFrom(plugin.GetType())
Equals only handles cases where types are exactly the same. IsAssignableFrom also handles the case where ofType may be a type that your plugin inherits from, or an interface that is implemented.
Not an answer but too long for a comment...
Are you sure the issue is not in how you call the method or populate your collection?
The comparison itself should be ok, as demonstrated by this simplified version of your code:
class A {}
bool TestType(A item, Type ofType)
{
return item.GetType().Equals(ofType);
}
now:
Console.WriteLine(TestType(new A(), typeof(A))); // True
Console.WriteLine(TestType(new A(), typeof(string))); // False
EDIT
I think #vcsjones is right. You're trying to compare a derived class to a base class.
In the line foreach (CompositionPlugin plugin in CompositionPlugins) you're declaring plugin to be a CompositionPlugin but in the client code you're comparing it with typeof(MyCompositionPlugin). (RE-EDIT no, I'm wrong, your case corresponds to the 4th of my Console.WriteLines that returns true)
See this example with a truth table for Equals in a scenario similar to yours:
class CompositionPlugin {}
class MyCompositionPlugin : CompositionPlugin {}
// Define other methods and classes here
bool TestType(CompositionPlugin item, Type ofType)
{
return item.GetType().Equals(ofType);
}
now
Console.WriteLine(TestType(new CompositionPlugin(),
typeof(CompositionPlugin))); //True
Console.WriteLine(TestType(new CompositionPlugin(),
typeof(MyCompositionPlugin))); //False
Console.WriteLine(TestType(new MyCompositionPlugin(),
typeof(CompositionPlugin))); //False
Console.WriteLine(TestType(new MyCompositionPlugin(),
typeof(MyCompositionPlugin))); //True
use the keyword is
if (plugin is ofType)
return plugin;
EDIT:
I have to go with #vcsjones on this one. Use the isassignablefrom function.
But if you really think it should work, what I always do is create quick function to write debug text to file.
public class Framework {
public IList<CompositionPlugin> CompositionPlugins = new List<CompositionPlugin>();
public CompositionPlugin GetCompositionPlugin(Type ofType)
{
using(var writer = System.IO.File.CreateText(#"C:\test.log"))
{
writer.WriteLine("ofType: " + ofType.toString());
foreach (CompositionPlugin plugin in CompositionPlugins)
{
writer.WriteLine("plugin: " + plugin.GetType().toString());
if (plugin.GetType().Equals(ofType))
return plugin;
}
}
throw new ArgumentException("A composition plugin of type " + ofType.FullName + " could not be found");
}
}
Turns out, the information I initially left out of the question, deeming it not important, was so after all.
The MyCompositionPlugin and CompositionPlugin are both defined in different assemblies, that the executing program loads dynamically at runtime.
The .NET-Runtime now consideres a type loaded from a different assembly another than the one referenced by the executing assembly, rendering the MyCompositionPlugin-Type in the Program to be considered unequal to the MyCompositionPlugin loaded from another assembly, even if they are actually the same.
The solution to comparing the two for equality (in that they are the same "Class" in the common sense) is to break it down to a string-equality of the defining assemblies, which is arguably dirty, but does the trick.
public CompositionPlugin GetCompositionPlugin(Type ofType)
{
foreach (CompositionPlugin plugin in CompositionPlugins)
if (ofType.AssemblyQualifiedName.Equals(plugin.GetType().AssemblyQualifiedName))
return plugin;
throw new ArgumentException("A composition plugin of type " + ofType.FullName + " could not be found");
}
Cudos to Paolo Falabella who pointed to this question
Your problem is most likely due to the fact that the assembly is loaded a second time and therefor the equality fails, it's best to avoid the assembly being loaded again, so you can use normal type comparison instead of needing to use that dirty string comparison. I had the same problem, my plugin interface was defined in a separate assembly, this assembly was present in the start up folder but also in the plugin folder from which the plugin assemblies where dynamically loaded, by consequence interface type equality failed. I removed the interface dll from the plugin folder and after that everything worked as expected.
Related
My class requires additional information to properly output its status, so I've added a custom PrintSelf method taking the appropriate parameters.
However, I'm afraid there are still calls to ToString in my large project, which were not replaced by the new method. How can I find those improper calls to ToString?
I'm using VS 2015, but it does not seem to have this ability.
Throwing an exception in ToString would be an obvious way, but I don't want to do that for two reasons:
ToString can still perform a different job and output something not depending on the added parameter.
There is no way to get full code coverage, meaning it would only find a few instances of implicit calls, but not (reliably) all of them.
To override ToString and log the caller you can do like this
public override string ToString()
{
StackTrace stackTrace = new StackTrace();
StackFrame[] stackFrames = stackTrace.GetFrames();
StackFrame callingFrame = stackFrames[1];
MethodInfo method = callingFrame.GetMethod();
YourLogingMethod(method.DeclaringType.Name + "." + method.Name);
return base.ToString();
}
You can make usage of the Obsolete Attribute :
public class MyFirstClass
{
//true or false parameters indicates whether to throw
// a compile error (true) or warning (false)
[Obsolete("Please use the method PrintSelf() instead of ToString()", false)]
public overrides string ToString()
{
//Whatever code you want here
return "";
}
}
public class MySecondClass
{
public void Test()
{
mfc = new MyFirstClass();
mfc.ToString(); //Here you will get a compiler warning
}
}
So this will let you know inside Visual Studio of all the calls made to this function. Since it is only a warning, it is still possible to use it.
(note : Sorry if the syntax is not correct, I'm normally a VB .Net developper, feel free to correct it if needed.)
I'm quite sure I'm missing some constraint or caveat somewhere, but here's my situation. Assume I have a class that I want to have a proxy for, like the following:
public class MyList : MarshalByRefObject, IList<string>
{
private List<string> innerList;
public MyList(IEnumerable<string> stringList)
{
this.innerList = new List<string>(stringList);
}
// IList<string> implementation omitted for brevity.
// For the sake of this exercise, assume each method
// implementation merely passes through to the associated
// method on the innerList member variable.
}
I want to create a proxy for that class, so that I can intercept method calls and perform some processing on the underlying object. Here is my implementation:
public class MyListProxy : RealProxy
{
private MyList actualList;
private MyListProxy(Type typeToProxy, IEnumerable<string> stringList)
: base(typeToProxy)
{
this.actualList = new MyList(stringList);
}
public static object CreateProxy(IEnumerable<string> stringList)
{
MyListProxy listProxy = new MyListProxy(typeof(MyList), stringList);
object foo = listProxy.GetTransparentProxy();
return foo;
}
public override IMessage Invoke(IMessage msg)
{
IMethodCallMessage callMsg = msg as IMethodCallMessage;
MethodInfo proxiedMethod = callMsg.MethodBase as MethodInfo;
return new ReturnMessage(proxiedMethod.Invoke(actualList, callMsg.Args), null, 0, callMsg.LogicalCallContext, callMsg);
}
}
Finally, I have a class that consumes the proxied class, and I set the value of the MyList member via reflection.
public class ListConsumer
{
public MyList MyList { get; protected set; }
public ListConsumer()
{
object listProxy = MyListProxy.CreateProxy(new List<string>() { "foo", "bar", "baz", "qux" });
PropertyInfo myListPropInfo = this.GetType().GetProperty("MyList");
myListPropInfo.SetValue(this, listProxy);
}
}
Now, if I try to use reflection to access the proxied object, I run into problems. Here is an example:
class Program
{
static void Main(string[] args)
{
ListConsumer listConsumer = new ListConsumer();
// These calls merely illustrate that the property can be
// properly accessed and methods called through the created
// proxy without issue.
Console.WriteLine("List contains {0} items", listConsumer.MyList.Count);
Console.WriteLine("List contents:");
foreach(string stringValue in listConsumer.MyList)
{
Console.WriteLine(stringValue);
}
Type listType = listConsumer.MyList.GetType();
foreach (Type interfaceType in listType.GetInterfaces())
{
if (interfaceType.IsGenericType && interfaceType.GetGenericTypeDefinition() == typeof(ICollection<>))
{
// Attempting to get the value of the Count property via
// reflection throws an exception.
Console.WriteLine("Checking interface {0}", interfaceType.Name);
System.Reflection.PropertyInfo propInfo = interfaceType.GetProperty("Count");
int count = (int)propInfo.GetValue(listConsumer.MyList, null);
}
else
{
Console.WriteLine("Skipping interface {0}", interfaceType.Name);
}
}
Console.ReadLine();
}
}
Attempting to call GetValue on the Count property via reflection throws the following exception:
An exception of type 'System.Reflection.TargetException' occurred in
mscorlib.dll but was not handled in user code
Additional information: Object does not match target type.
When attempting to get the value of the Count property, apparently the framework is calling down into System.Runtime.InteropServices.WindowsRuntime.IVector to call the get_Size method. I'm not understanding how this call fails on the underlying object of the proxy (the actual list) to make this happen. If I'm not using a proxy of the object, getting the property value works fine via reflection. What am I doing wrong? Can I even do what I'm trying to accomplish?
Edit: A bug has been opened regarding this issue at the Microsoft Connect site.
I think this may be a bug in the .Net framework. Somehow the RuntimePropertyInfo.GetValue method is picking the wrong implementation for the ICollection<>.Count property, and it appears to have to do with WindowsRuntime projections. Perhaps the remoting code was redone when they put the WindowsRuntime interop in the framework.
I switched the framework to target .Net 2.0 since I thought if this was a bug, it shouldn't be in that framework. When converting, Visual Studio removed the "Prefer 32 bit" check on my console exe project (since this doesn't exist in 2.0). It runs without exception when this is not present.
In summary, it runs on .Net 2.0 in both 32 and 64 bit. It runs on .Net 4.x in 64 bit. The exception is thrown on .Net 4.x 32 bit only. This sure looks like a bug. If you can run it 64-bit, that would be a workaround.
Note that I've installed .Net 4.6, and this replaces much of the .Net framework v4.x. It could be this is where the problem is introduced; I can't test until I get a machine that doesn't have .Net 4.6.
Update: 2015-09-08
It also happens on a machine with only .Net 4.5.2 installed (no 4.6).
Update: 2015-09-07
Here's a smaller repro, using your same classes:
static void Main(string[] args)
{
var myList = MyListProxy.CreateProxy(new[] {"foo", "bar", "baz", "quxx"});
var listType = myList.GetType();
var interfaceType = listType.GetInterface("System.Collections.Generic.ICollection`1");
var propInfo = interfaceType.GetProperty("Count");
// TargetException thrown on 32-bit .Net 4.5.2+ installed
int count = (int)propInfo.GetValue(myList, null);
}
I've also tried the IsReadOnly property, but it appears to work (no exception).
As to the source of the bug, there are two layers of indirection around properties, one being the remoting, and the other being a mapping of metadata structures called MethodDefs with the actual runtime method, known internally as a MethodDesc. This mapping is specialized for properties (as well as events), where additional MethodDescs to support the property's get/set PropertyInfo instances are known as Associates. By calling PropertyInfo.GetValue we go through one of these Associate MethodDesc pointers to the underlying method implementation, and remoting does some pointer math to get the correct MethodDesc on the other side of the channel. The CLR code is very convoluted here, and I don't have enough experience of the in-memory layout of the MethodTable which holds these MethodDesc records which remoting uses (or the mapping it uses to get to the MethodTable?), but I'd say it's a fair guess that remoting is grabbing the wrong MethodDesc via some bad pointer math. That's why we see a similar but unrelated (as far as your program) MethodDesc - UInt32 get_Size of IVector<T> being invoked on the call:
System.Reflection.RuntimeMethodInfo.CheckConsistency(Object target)
System.Reflection.RuntimeMethodInfo.InvokeArgumentsCheck(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
System.Reflection.MethodBase.Invoke(Object obj, Object[] parameters)
ConsoleApplication1.MyListProxy.Invoke(IMessage msg) Program.cs: line: 60
System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type)
System.Runtime.InteropServices.WindowsRuntime.IVector`1.get_Size()
System.Runtime.InteropServices.WindowsRuntime.VectorToCollectionAdapter.Count[T]()
This is a pretty interesting CLR bug, some of its guts are showing in the mishap. You can tell from the stack trace that it is trying to call the VectorToCollectionAdapter's Count property.
This class is rather special, no instance of it ever gets created. It is part of the language projection that was added in .NET 4.5 that makes WinRT interface types look like .NET Framework types. It is pretty similar to the SZArrayHelper class, an adapter class that helps implement the illusion that non-generic arrays implement generic interface types like IList<T>.
The interface mapping at work here is for the WinRT IVector<T> interface. As noted in the MSDN article, that interface type is mapped to IList<T>. The internal VectorToListAdapter class takes care of the IList<T> members, VectorToCollectionAdapter tackles the ICollection<T> members.
Your code forces the CLR to find the implementation of ICollection<>.Count and that could either be a .NET class implementing it as normal or it could be a WinRT object that exposes it as IVector<>.Size. Clearly the proxy you created gives it a headache, it incorrectly decided for the WinRT version.
How it is supposed to figure out which is the correct choice is pretty murky. After all, your proxy could be a proxy for an actual WinRT object and then the choice it made would be correct. This could well be a structural problem. That it acts so randomly, the code does work in 64-bit mode, is not exactly inspiring. VectorToCollectionAdapter is very dangerous, note the JitHelpers.UnsafeCast calls, this bug is potentially exploitable.
Well, alert the authorities, file a bug report at connect.microsoft.com. Let me know if you don't want to take the time and I'll take care of it. A workaround is hard to come by, using the WinRT-centric TypeInfo class to do the reflection did not make any difference. Removing the jitter forcing so it runs in 64-bit mode is a band-aid but hardly a guarantee.
we are currently hacking around this problem with this brittle intervention (apologies for code):
public class ProxyBase : RealProxy
{
// ... stuff ...
public static T Cast<T>(object o)
{
return (T)o;
}
public static object Create(Type interfaceType, object coreInstance,
IEnforce enforce, string parentNamingSequence)
{
var x = new ProxyBase(interfaceType, coreInstance, enforce,
parentNamingSequence);
MethodInfo castMethod = typeof(ProxyBase).GetMethod(
"Cast").MakeGenericMethod(interfaceType);
return castMethod.Invoke(null, new object[] { x.GetTransparentProxy() });
}
public override IMessage Invoke(IMessage msg)
{
IMethodCallMessage methodCall = (IMethodCallMessage)msg;
var method = (MethodInfo)methodCall.MethodBase;
if(method.DeclaringType.IsGenericType
&& method.DeclaringType.GetGenericTypeDefinition().FullName.Contains(
"System.Runtime.InteropServices.WindowsRuntime"))
{
Dictionary<string, string> methodMap = new Dictionary<string, string>
{ // add problematic methods here
{ "Append", "Add" },
{ "GetAt", "get_Item" }
};
if(methodMap.ContainsKey(method.Name) == false)
{
throw new Exception("Unable to resolve '" + method.Name + "'.");
}
// thanks microsoft
string correctMethod = methodMap[method.Name];
method = m_baseInterface.GetInterfaces().Select(
i => i.GetMethod(correctMethod)).Where(
mi => mi != null).FirstOrDefault();
if(method == null)
{
throw new Exception("Unable to resolve '" + method.Name +
"' to '" + correctMethod + "'.");
}
}
try
{
if(m_coreInstance == null)
{
var errorMessage = Resource.CoreInstanceIsNull;
WriteLogs(errorMessage, TraceEventType.Error);
throw new NullReferenceException(errorMessage);
}
var args = methodCall.Args.Select(a =>
{
object o;
if(RemotingServices.IsTransparentProxy(a))
{
o = (RemotingServices.GetRealProxy(a)
as ProxyBase).m_coreInstance;
}
else
{
o = a;
}
if(method.Name == "get_Item")
{ // perform parameter conversions here
if(a.GetType() == typeof(UInt32))
{
return Convert.ToInt32(a);
}
return a;
}
return o;
}).ToArray();
// this is where it barfed
var result = method.Invoke(m_coreInstance, args);
// special handling for GetType()
if(method.Name == "GetType")
{
result = m_baseInterface;
}
else
{
// special handling for interface return types
if(method.ReturnType.IsInterface)
{
result = ProxyBase.Create(method.ReturnType, result, m_enforce, m_namingSequence);
}
}
return new ReturnMessage(result, args, args.Length, methodCall.LogicalCallContext, methodCall);
}
catch(Exception e)
{
WriteLogs("Exception: " + e, TraceEventType.Error);
if(e is TargetInvocationException && e.InnerException != null)
{
return new ReturnMessage(e.InnerException, msg as IMethodCallMessage);
}
return new ReturnMessage(e, msg as IMethodCallMessage);
}
}
// ... stuff ...
}
m_coreInstance here is the object instance that the proxy is wrapping.
m_baseInterface is the interface the object is to be used as.
this code intercepts the call(s) made in VectorToListAdapter and VectorToCollectionAdapter and converts it back into the original via that methodMap dictionary.
the part of the conditional:
method.DeclaringType.GetGenericTypeDefinition().FullName.Contains(
"System.Runtime.InteropServices.WindowsRuntime")
makes sure it only intercepts calls that come from stuff in the System.Runtime.InteropServices.WindowsRuntime namespace - ideally we would target the types directly but they are inaccessible - this should probably be changed to target specific class names in the namespace.
the parameters are then cast into the appropriate types and the method is invoked. the parameter conversions appear to be necessary as the incoming parameter types are based on the parameter types of the method calls from the objects in the System.Runtime.InteropServices.WindowsRuntime namespace, and not the parameters of the method calls to the original object types; i.e. the original types before the objects in the System.Runtime.InteropServices.WindowsRuntime namespace hijacked the mechanism.
for example, the WindowsRuntime stuff intercepts the original call to get_Item, and converts it into a call to the Indexer_Get method: http://referencesource.microsoft.com/#mscorlib/system/runtime/interopservices/windowsruntime/vectortolistadapter.cs,de8c78a8f98213a0,references. this method then calls the GetAt member with a different parameter type, which then calls GetAt on our object (again with a different parameter type) - this is the call we hijack in our Invoke() and convert it back into the original method call with the original parameter types.
it would be nice to be able to reflect over VectorToListAdapter and VectorToCollectionAdapter to extract all their methods and the nested calls they make, but these classes are unfortunately marked as internal.
this works for us here, but i'm sure its full of holes - it is a case of trial and error, running it to see what fails and then adding in the required dictionary entries/parameter conversions. we are continuing the search for a better solution.
HTH
First of all: I am using C++-CLI, but, as users have asked, I think this also applies to C#, that's why I have added this tag.
I have following function
static CFiducial^ CFiducial::GetCommonBase (array<CFiducial^>^ i_aoFiducial)
{
if (!i_aoFiducial || i_aoFiducial->Length <= 0)
return gcnew CFiducial;
CFiducial^ oFiducial;
for (int iCnt = 0; iCnt < i_aoFiducial->Length; iCnt++)
{
if (!i_aoFiducial[iCnt]) continue;
if (oFiducial)
oFiducial= oFiducial->GetCommonBase (i_aoFiducial[iCnt]);
else
i_aoFiducial[iCnt]->CopyTo (oFiducial);
}
return oFiducial;
}
which is used in identical way in multiple classes.
(There is another member function GetCommonBase (CFiducial^) which compares the member variables content to the content of the members of the passed object and returns a new object with only those members set that are equal. See below for an example).
I now was going to outsource all implementations of this static function and move them to a central location, having only one implementation there. I have tried generics and interfaces, but both fail:
generics don't work because I can't call methods on generics.
interfaces fail because the functions defined in the interface need to take an instance of the object that the interface later is bound to.
This is the interface was going to use:
public interface class ICommonBase
{
ICommonBase^ GetCommonBase (ICommonBase^);
void CopyTo (ICommonBase^%);
};
I could not implement GetCommonBase (ICommonBase^) in the actual classes, I need to implement GetCommonBase (CLASSNAME^), but that does not match the definition in the interface.
Is there a way around this?
Is there another alternative to interface and generics?
I still want to avoid copy and paste of that function into each class where I need it.
Edit
Sample of the member function GetCommonBase(), which is from a different class, where inheritance is actually used, therefore the name. The problem described above has nothing to do with inheritance though.
CImageObject^ CImgObjCircle::GetCommonBase (CImageObject^ i_oImageObject)
{
CImageObject^ oImgObj;
if (!i_oImageObject)
{
this->CopyTo (oImgObj);
return oImgObj;
}
oImgObj = CImageObject::GetCommonBase (i_oImageObject);
CImgObjCircle^ i_oImgObj = dynamic_cast<CImgObjCircle^>(i_oImageObject);
if (!i_oImgObj)
return oImgObj;
CImgObjCircle^ oImgObjLocal = gcnew CImgObjCircle;
oImgObj->CopyTo (oImgObjLocal);
if (m_oDiameterX == i_oImgObj->m_oDiameterX) oImgObjLocal->m_oDiameterX = m_oDiameterX;
return oImgObjLocal;
}
The context is as follows
I want to refactor code by moving it around to different projects
Some of this code comprises of serializable DTOs that are used to
send and receive data across multiple endpoints
If I move the code around, serialization breaks (therefore it is not
backward compatible with older versions of my application)
A solution to this problem is the SerializationBinder which allows me to "redirect" in a sense from one type to another.
I therefore want to create a SerializationBinder to meet this need. However it must do so by meeting the following requirements
The inputs to the SerializationBinder should be a list of old type
to new type mappings. The mapping should include the old assembly
name (no version, no public key token) and the old full name of the
type (namespace and name) as well as the new assembly name and new
full name of the type
For the types that are in the inputs, version numbers of assemblies should be ignored
It should handle generics if my types happen to be in generics
(List, Dictionary, etc) without needing to include the generics in the inputs
For anything that is not in the inputs (i.e. types that have not
moved or .NET types like dataset for example) it should default to
using the out of the box algorithm of the binary serializer
Is this possible or am I dreaming? Is there something out there that already does this? I would assume this is a common problem.
So far I see no easy way of doing 3 and no way at all of doing 4.
Here is an attempt
public class SmartDeserializationBinder : SerializationBinder
{
/// <summary>
/// Private class to handle storing type mappings
/// </summary>
private class TypeMapping
{
public string OldAssemblyName { get; set; }
public string OldTypeName { get; set; }
public string NewAssemblyName { get; set; }
public string NewTypeName { get; set; }
}
List<TypeMapping> typeMappings;
public SmartDeserializationBinder()
{
typeMappings = new List<TypeMapping>();
}
public void AddTypeMapping(string oldAssemblyName, string oldTypeName, string newAssemblyName, string newTypeName)
{
typeMappings.Add(new TypeMapping()
{
OldAssemblyName = oldAssemblyName,
OldTypeName = oldTypeName,
NewAssemblyName = newAssemblyName,
NewTypeName = newTypeName
});
}
public override Type BindToType(string assemblyName, string typeName)
{
//Need to handle the fact that assemblyName will come in with version while input type mapping may not
//Need to handle the fact that generics come in as mscorlib assembly as opposed to the assembly where the type is defined.
//Need to handle the fact that some types won't even be defined by mapping. In this case we should revert to normal Binding... how do you do that?
string alternateAssembly = null;
string alternateTypeName = null;
bool needToMap = false;
foreach (TypeMapping mapping in typeMappings)
{
if (typeName.Contains(mapping.OldTypeName))
{
alternateAssembly = mapping.NewAssemblyName;
alternateTypeName = mapping.NewTypeName;
needToMap = true;
break;
}
}
if (needToMap)
{
bool isList = false;
if (typeName.Contains("List`1"))
isList = true;
// other generics need to go here
if (isList)
return Type.GetType(String.Format("System.Collections.Generic.List`1[[{0}, {1}]]", alternateTypeName, alternateAssembly));
else
return Type.GetType(String.Format("{0}, {1}", alternateTypeName, alternateAssembly));
}
else
return null; // this seems to do the trick for binary serialization, but i'm not sure if it is supposed to work
}
}
This could work (instead of your override).
public override Type BindToType(string assemblyName, string typeName)
{
var m = Regex.Match(typeName, #"^(?<gen>[^\[]+)\[\[(?<type>[^\]]*)\](,\[(?<type>[^\]]*)\])*\]$");
if (m.Success)
{ // generic type
var gen = GetFlatTypeMapping(m.Groups["gen"].Value);
var genArgs = m.Groups["type"]
.Captures
.Cast<Capture>()
.Select(c =>
{
var m2 = Regex.Match(c.Value, #"^(?<tname>.*)(?<aname>(,[^,]+){4})$");
return BindToType(m2.Groups["aname"].Value.Substring(1).Trim(), m2.Groups["tname"].Value.Trim());
})
.ToArray();
return gen.MakeGenericType(genArgs);
}
return GetFlatTypeMapping(assemblyName,typeName);
}
Then you just have to implement your way the function GetFlatTypeMapping (not worrying of about generic arguments).
What you will have to do is to return typeof(List<>) and typeof(Dictionary<,>) (or any other generic you would like to use) when asked.
nb: I said typeof(List<>) ! not typeof(List<something>) ... that's important.
disclaimer: because of the regex "(?[^]]*)", this snipped does not support nested generic types like List<List<string>> ... you will have to tweak it a bit to support it !
Although it might not answer your question this might solve your problem:
I was able to serialize all but one assembly required for deserialization alongside an object structure. The trick is to use reflection to inspect your object structure for types and the assemblies they are defined in. You can then write the assemblies as binary data into an object on serialization and load them into an AppDomain where you can use them to deserialize the rest of the object structure.
You have to put the AppDomain handling, the root object and some base classes into an assembly that won't change and everything else is defined in assemblies depending on this "anchor".
Upsides:
serialization does not break as long as the anchor assembly does not change
can be used for obfuscation, e.g. some required assembly can be hidden in any file
can be used for online updates, e.g. ship the assembly with any data
as far as I'm concerned no problem with generics
Downsides:
security issues as bootstrapping could be used to inject code (some extra work for assembly SecurityEvidences)
AppDomain borders are some work to cross
blows up your serialized data (ok, for files - bad, for communication)
But hey, communication does not break unless you have different client versions and then you can do the bootstrapping on handshake.
Sorry, I can't provide the code as it's too complex and too specific. But I share some insights in these other answers of mine:
difference between DataContract attribute and Serializable attribute in .net
Need to hookup AssemblyResolve event when DisallowApplicationBaseProbing = true
Is it a hard requirement that you MUST be using the BinaryFormatter for your de/serialization?
If it's not a hard requirement for you to be using the BinaryFormatter to do your serialization, consider alternative serialization methods, such as JSON.Net or ProtoBuf.Net.
Either one of these will create platform- and version-independent serializations of your data.
Alternatively, you can perform binary serialization yourself (which is both faster and smaller than the BinaryFormatter, but generally more code-intensive as you have to be sure to write the serializer and deserializer essentially identically to each other.
If you must use BinaryFormatter, be sure to construct it with FormatterAssemblyStyle.Simple, to get around versioning problems. That tells it to not do the pedantic check of the assembly version.
I am having some trouble with assemblies and DLL's.
instrument_ is declared as an object and I'm creating an instance of "PP150" from the dll whose path is specified by path_.
string className = ContineoProperties.getSingleton().getClassName(path_);
assembly_ = Assembly.LoadFrom(path_);
Type classType = assembly_.GetType("Instrument." + className);
instrument_ = Activator.CreateInstance(classType);
Later I to call the method isntrument_.instrumentCommand(cmd.getCommandName())
The error I get is with when i call the method.
'object' does not contain a definition for 'instrumentCommand'
The isntrument_ is created fine. its just the method call that's giving me a problem. The method does exist in the "PP150.dll". Do I need some DLLImport to allow it to recognize it as a function?
Thanks,
P
If object type is not known in compile time,
To call a method defined on an object, you must use Reflection.
MethodInfo mInfo = classType.GetMethod("instrumentCommand");
mInfo.Invoke(instrument_, new Object[] { _parameters});
The compiler is never going to recognize the methods on a type that you are loading via reflection (e.g. using Assembly.GetType() and Activator.CreateInstance()). Unless you have the type metadata available at build time, you will always get that error if you try to call methods that are not defined on Object itself.
You have two options for making that kind of method call. Both of them require you to give up type safety, the only difference is the amount of work required. In both cases, if you make a mistake, the compiler will not tell you -- you will get a runtime exception instead.
Declare instrument_ as dynamic instead of object. This, obviously, only works in .NET 4.0, but it accomplishes exactly what you're trying to do. The method call will be dispatched at runtime, so as long as the instance that instrument_ references actually has a method call with the appropriate name, it will work.
Use reflection to call the method. You're already using reflection to load the type, so you are halfway there. You would need to add something like this:
// The array of types is the parameter list; assuming instrumentCommand takes
// a string it would look like this:
MethodInfo method = classType.GetMethod("instrumentCommand", new Type[] { typeof(string) });
method.Invoke(instrument_, new object[] { cmd.getCommandName() });
This happens because Activator.CreateInstance returns an object. I would create a separate DLL for the interface which is implemented by the class you want to instantiate. Both the DLL containing this class, and the executable should reference the DLL containing the interface. This way you could cast the object returned by Activator.CreateInstance to the interface, and call its methods:
IInstrument.dll:
interface IInstrument
{
void instrumentCommand(string cmd);
}
Instrument.dll (add IInstrument.dll as reference):
class Instrument : IInstrument
{
public void instrumentCommand(string cmd)
{
// ... implementation ...
}
}
InstrumentApp.exe (add IInstrument.dll as reference):
class Program
{
public static void Main()
{
// ... load Instrument.dll into assembly object ...
// ... load the type from the assembly ...
IInstrument instrument_ = (IInstrument)Activator.CreateInstance(classType);
instrument_.instrumentCommand(cmd.getCommandName());
}
}
The most simple thing would be to link agains PP150.
If you did link against the dll you must use Assembly.LoadFile or Assembly.Load and not LoadFrom because the last one will cause the assembly load to load your assembly in the LoadFrom loader context which will alter type identity.
Suppose you load the Type T from Assembly A via LoadFrom and you link against A as well.
object CreateTypeFrom()
{
var A = Assembly.LoadFrom(#"xxxx");
return A.CreateInstance("T");
}
void Test()
{
object t = CreateTypeFrom();
T RealT = new T(); // no prob
T Castedt = (T)t; // this will throw an InvalidCastException
T isNull = t as T; // this will result in a null instance
}
As you can see although you did create two times an instance of T they cannot be casted to due to different loader context which will make the type pretty useless.
To get rid of these things you could simply use Reflection to create a proxy type which will forward your calls to the proxy type. If you are using .NET 4 you can take advantage of the DLR to find the best matching methods at runtime. The code below creats a Version object and returns it as dynamic object. Then I do call the Major property to an integer and print it out to console. This does work with no exceptions nor compile time errors if you are using .NET 4 or later.
dynamic CreateTypeFrom()
{
var assembly = typeof(string).Assembly;
return assembly.CreateInstance("System.Version", true, BindingFlags.CreateInstance, null, new object[] { 1, 2, 3, 4 }, null, null);
}
[TestMethod]
public void Test()
{
var t = CreateTypeFrom();
int major = t.Major;
Console.WriteLine(major);
}