I have an application containing a certain number of custom TypeConverter. They work well when used in the context of XML parsing.
On another side I am invoking class constructors (based on some previously defined XML tag) using the following (that works so far):
Type MyClass = TypeDelegator.GetType( FullClassName ); // Get the class type
if( MyClass != null ) { // If we find a matching type
ConstructorInfo[] CtorInfos = MyClass.GetConstructors(); // Get constructor infos
if( CtorInfos != null ) { // If we could find a ctor
if( CtorInfos.Length == 1 ) { // We expect 1 Ctor with args
CtorInfos[0].Invoke( new object[] { CtorArg1, CtorArg2 } );
}
}
}
Now I am trying to reuse the TypeConverter from within the invoked constructor. But it does not seems to work. I have been using the regular GetConverter method:
MyType myVar = (MyType)TypeDescriptor.GetConverter( typeof( MyType ) ).ConvertFrom( "1;2;3;4" );
The type class is similar to the following:
public class MyType {
private int PrivateField;
public MyType() { }
public MyType( String Arg1, Char Arg2 ) { }
public int Prop1 { get { return ( 4 ); } }
public int Prop2 {
get { return ( PrivateField ); }
set { PrivateField = value; }
}
}
Digging into the problem it seems that TypeDescriptor.GetConverter does not find the custom type converter (that are part of the same assembly).
var debugVar = TypeDescriptor.GetConverter( typeof( MyType ) );
always return a TypeConverter as opposed to an expected "TypeConverterMyType". That make me think "TypeConverterMyType" is not accessible probably because of the invoke.
[Update]
It seems that the TypeDescriptor.GetConverter works for enum types but not for class based type. I think I am passing next to an important part of the puzzle... :?
So the question :
1- Is it actually possible to access a custom type converter from an invoked method?
2- Is there a better/smarter way to handle this?
3- I am using .Net 3.5, should I expect the same behaviour if I upgrade to 4.0 or 4.5?
Related
I have a Blazor service which accepts any draggable object, boxed as object. It could be a dragged user, or a classroom, or a scheduled item or almost anything else I might dream up later.
When the draggable is dropped into any component which supports dropping, the component needs to check if it's the right kind of object. For example, the StudentList.razor component will only accept drops if they are IdentityUser or the duple (IdentityUser, string) where the string might be a role name or some other arbitrary info (TBD later):
<div class="class-students-drop" #ondrop="_=>HandleStudentDrop()">
. . .
</div>
#code {
async Task HandleStudentDrop()
{
if (DM.GetItem() is IdentityUser Person)
{
// Do generic user thing (works fine)
}
if (DM.GetItem() is (IdentityUser person,string role) RolePerson)
{
// Do thing based on specified role
// Error (active) CS1061 'object' does not contain a definition for 'Deconstruct' and no accessible extension method 'Deconstruct' accepting a first argument of type 'object' could be found
}
}
}
I can pattern-check a class instance like IdentityUser, but I can't figure out how to check if the boxed object fits a particular duple form.
My question: what's the right syntax (if any) to check the signature of a duple using the 'is' keyword?
I've seen examples with pattern-matching duples with values using switch, but I really just want to check if the boxed object is an `(IdentityUser, string) duple.
My references:
https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/functional/pattern-matching
https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/functional/deconstruct
Remove the identifier after the tuple type definition:
private object Test()
{
return (new IdentityUser("Me"), "Test");
}
object o = Test();
if(o is (IdentityUser user, string role))
{
Console.WriteLine(user.UserName + " " + role);
}
The quick-and-dirty approach should work:
if( DM.GetItem() is ValueTuple<IdentityUser,String> rolePerson )
{
}
...though you'll lose member names: so use Item1 and Item2 respectively.
Another (better) approach is to use an extension method over Object:
public static class MyExtensions
{
public static Boolean IsTuple<TTuple>( this Object obj, [NotNullWhen(true)] out TTuple? tuple )
where TTuple : struct, ITuple
{
if( obj is TTuple ok )
{
tuple = ok;
return true;
}
else
{
tuple = null;
return false;
}
}
}
Example usage:
void Main()
{
( Int32 x, String y ) tuple = ( 123, "abc" );
Object? obj = tuple;
if( obj.IsTuple( out ( Int32 x, Single y )? notThis ) )
{
notThis.Dump( "Not this" );
}
else if( obj.IsTuple( out ( Int32 x, String y )? thisWorks ) )
{
thisWorks.Dump( "OK" );
}
else
{
"".Dump( "Nor this either." );
}
}
Screenshot proof it works:
In your case, it'd be:
async Task HandleStudentDrop()
{
if (DM.GetItem() is IdentityUser Person)
{
// Do generic user thing (works fine)
}
else if (DM.GetItem().IsTuple( out ( IdentityUser person, String role )? ok ) )
{
<p><b>Name:</b> #( ok.Value.person.Name )</p>
<p><b>Role:</b> #( ok.Value.role )</p>
}
}
I have a set of classes which are defined and populated from parsed XML.
As part of this, I want to be able to dynamically instantiate collection classes for particular types, as specified by the XML (in this case, to manage / instantiate exceptions).
So I have a class, approximately defined as follows:
public class ExceptionGroup<T> : BaseCollectionClass<Exception> where T : Exception
{
// contents of this class don't really matter
}
When I process the XML, I will have the name of the Exception-derived type contained in a string (i.e. "ArgumentNullException", "InvalidDataException", "IndexOutOfRangeException" etc.), as well as the content (message) that will be used to populate the generated Exception (also a string) when/if it's thrown.
So, to get where I'm trying to go, I first have implemented a couple of relevant static classes (defined elsewhere):
// Recursively determines if a supplied Type is ultimately derived from the Exception class:
public static bool IsException( Type type ) =>
(type == typeof( object )) ? false : (type == typeof(Exception)) || IsException( type.BaseType );
// Figures out if the specified string contains the name of a recognized Type
// and that the Type itself is genuinely derived from the Exception base class:
public static Type DeriveType( string name )
{
// If the string is null, empty, whitespace, or malformed, it's not valid and we ignore it...
if ( !string.IsNullOrWhiteSpace( name ) && Regex.IsMatch( name, #"^([a-z][\w]*[a-z0-9])$", RegexOptions.IgnoreCase ) )
try
{
Type excType = System.Type.GetType( name );
if ( IsException( excType ) ) return excType;
}
catch { }
// The type could not be determined, return null:
return null;
}
Using these classes, I can take an input string and end up with a known, existing, C# Type class (presuming the input string is valid) that's derived from the Exception class. Now I want to build a factory method that can create new ExceptionGroup<T> objects where "T" is the object type that's derived from the orignal string.
I've kind of managed this by using the dynamic type as the factory's return type as follows:
public static dynamic CreateExceptionGroup( string exceptionTypeName )
{
Type excType = DeriveType( exceptionTypeName );
if ( !(excType is null) )
{
Type groupType = typeof( ExceptionGroup<> ).MakeGenericType( new Type[] { excType } );
return Activator.CreateInstance( groupType );
}
return null;
}
I'm quite uncomfortable with this though, both because I dislike the ambiguity / uncertainty of the result, and because subsequently working with that result can be more cumbersome/complex. I'd much rather actually specify the return type more concretely, and then appropriately cast/qualify it in some manner, for example (yes, I know that this isn't valid!):
public static ExceptionGroup<> CreateGroup( string exceptionTypeName )
{
Type excType = DeriveType( exceptionTypeName );
if ( !(excType is null) )
{
Type[] types = new Type[] { excType };
Type groupType = typeof( ExceptionGroup<> ).MakeGenericType( types );
return (ExceptionGroup<>)Activator.CreateInstance( groupType );
}
return null;
}
...but, of course ExceptionGroup<> isn't valid in this syntax/context. (Generates "CS7003: Unexpected use of an unbound generic name"), and neither is merely using ExceptionGroup (Generates: "CS0305: Using the generic type 'ExceptionGroup' requires 1 type arguments.")
So, IS there a way to do this with strong(er) typing, via some other syntax or mechanism, with more precisely described results, or is using dynamic, with all of the subsequent associated overhead, literally the only way to accomplish this?
While I hope that there may be a more simple/ succinct solution (and would love to see it if so!) some retrospective prompting by Sweeper led me to the realisation that I could essentially bypass the problem by injecting the overhead of a new abstract ancestor class:
public abstract class ExceptionGroupFoundation : BaseCollectionClass<Exception>
{
public ExceptionGroupFoundation( object[] args = null ) : base( args ) { }
// Implement necessary common accessors, methods, fields, properties etc here...
// (preferably "abstract" as/when/where possible)
}
... then deriving my generic class from that one:
public class ExceptionGroup<T> : ExceptionGroupFoundation where T : Exception
{
public ExceptionGroup( object[] args = null ) : base( args ) { }
// contents of this class don't really matter
}
... then declaring my factory method using the new abstract class as the return type:
public static ExceptionGroupFoundation CreateGroup( string exceptionTypeName )
{
Type excType = DeriveType( exceptionTypeName );
if ( !(excType is null) )
{
Type[] types = new Type[] { excType };
Type groupType = typeof( ExceptionGroup<> ).MakeGenericType( types );
return (ExceptionGroupFoundation)Activator.CreateInstance( groupType );
}
return null;
}
...to essentially arrive at the desired result, albeit somewhat cumbersomely/awkwardly.
I'm trying to create a delegate of a non-static method on my subclass, ExchangeA. I then want to pass this delegate method to another non-static method on the base-class Exchange. I say non-static because, firstly, they are, and secondly, the examples I have found online have mostly been with regard to static methods.
I have sought this as a resource.
public class Exchange {
public int retrieve( string szLevel, string szJson, Func<string, string, Instrument> delegateMethod) {
// ...
Instrument instrument = delegateMethod(szLevel, szJson)
// ...
return someInt;
}
}
public class ExchangeA : Exchange {
public Instrument instrumentDataProcess( string szLevel, szJson ) {
// ...
return someInstrument;
}
}
public class Instrument { ... }
public class DoStuff {
public static Exchange ExchangeHandler( my args ) {
Exchange oExchange = new ExchangeA(); // Could also be ExchangeB or ExchangeC being instantiated here
Type type = oExchange.GetType(); // type == ExchangeA
System.Reflection.MethodInfo methodInfo = type.GetMethod( "instrumentDataProcess" );
Func<string, string, Instrument> delegateFunc = (Func<string, string, Instrument>)Delegate.CreateDelegate(typeof(Func<string, string, Instrument>), methodInfo); // fails here at runtime
iRet = (int)type.InvokeMember( "retrieve", System.Reflection.BindingFlags.InvokeMethod, null, oExchange, new object[] { szLevel, szCurrencyBase, delegateFunc });
if (iRet == 0)
return nil;
return oExchange;
}
}
It fails during execution when calling the CreateDelegate() method.
The error says:
An exception of type 'System.ArgumentException' occurred in
mscorlib.dll but was not handled in user code
Additional information: Cannot bind to the target method because its
signature or security transparency is not compatible with that of the
delegate type.
Signature or security transparency? I already know the method signature because I defined it and I don't understand what security transparency means... I'm not usually a C# dev. All of my classes and functions are declared as public.
All comments and suggestions are valued! I want to learn the nonsense that is C#! ;)
Update:
Suggestions so far are to pass a method group as a parameter to my retrieve method. This is a new concept for me and I'm not sure how it's done. Does the parameter type Func<string, string, Instrument> still apply?
You don't need a delegate at all, you can just assign the method group:
public class Exchange {
public int retrieve( string szLevel, string szJson, Func<string, string, Instrument> delegateMethod) {
// ...
Instrument instrument = delegateMethod(szLevel, szJson)
// ...
return someInt;
}
}
public class ExchangeA : Exchange {
public Instrument instrumentDataProcess( string szLevel, szJson ) {
// ...
return someInstrument;
}
}
public class Instrument { ... }
public class DoStuff {
public static Exchange ExchangeHandler( my args ) {
Exchange oExchange = new ExchangeA(); // Could also be ExchangeB or ExchangeC being instantiated here
iRet = oExchange.retrieve(szLevel, szCurrencyBase, oExchange.instrumentDataProcess);
if (iRet == 0)
return nil;
return oExchange;
}
}
I am using reflection to invoke a method from another dll.That method takes a class object as input. How can i call that method.
I tried copying the same class to both dlls and i created an object of that class and passed it. It throws invliad conversion error in complie time itself.
Then i tried to let the function take an object as argument and then tried to cast it to the required class. It is throwing invalid cast exception in runtime.
this is what i tried
Test objTest = new Test("name","type");
Assembly assembly = Assembly.Load("MyProject.Components");
Type dllType = assembly.GetType("MynameSpace.MyClass");
if (dllType != null)
{
MethodInfo m = dllType.GetMethod("MyFunction");
object objdll;
objdll = Activator.CreateInstance(dllType);
object[] args = { objTest };
if ((m != null))
{
strReturnValue += (string)m.Invoke(objdll, args);
}
}
In the second dll the method is like
public string MyFunction(Test obj)
My problem is that Class Test is in
the other assembly(the classes are in
two different assemblies)
You have a bit of a design problem. You have an assembly (let's call it assembly A) containing the sample reflection code that you posted. You also have a second assembly (let's call it assembly B) that contains MyClass and Test.
The issue is that in your reflection code you are attempting to create an instance of the Test class so that you can pass it as a parameter to MyClass.MyFunction.
You mentioned that you copied the source code for the Test class into assembly A; that will not work. What you've done there is essentially create two different classes with the same name and same structure. Since the two classes are not the same as far as the CLR is concerned, you will get an invalid cast exception if you try to cast one to the other.
Given what you've posted so far, it seems to me that the most straightforward solution for your approach is to have a third assembly (let's call it assembly C) that contains components that are known to both assemblies A and B. Create a class library project in your solution, move the Test class into that project, get rid of any other occurrences of the Test class in the first two projects, and add references in both of the first two projects referencing the new project. Once you've done that, both assembly A and assembly B will be referencing the same class definition for the Test class and the sample code that you've posted will work.
Let me point out something, though. If the code in assembly A doesn't know enough about the code in assembly B in order to instantiate MyClass and call MyFunction directly (rather than through reflection), then how does it know enough about that code to know what parameters to pass? Does MyFunction have a common method signature that assembly A understands? If that's the case, then MyClass should probably implement an interface that assembly A knows about so that assembly A can invoke MyFunction directly, as shown below:
Assembly assembly = Assembly.Load("MyProject.Components");
Type dllType = assembly.GetType("MynameSpace.MyClass");
if (dllType != null)
{
IMyInterface instance = Activator.CreateInstance(dllType) as IMyInterface;
if (instance != null) // check if this object actually implements the IMyInterface interface
{
instance.MyFunction(objTest);
}
}
If that doesn't seem like an approach that you want, then there are other options. Since it seems like you don't want for assembly A to have a direct reference to assembly B, if you keep the Test class inside of assembly B, then there isn't any way for assembly A to have any knowledge of the Test class in order to construct one. In that case, you could use a factory pattern approach, basically so that assembly A knows of some kind of factory object that is capable of instantiating a Test object. Below is an example of an implementation:
I mentioned above about creating a third project. I would still recommend doing so. In my example, I've named mine "MyProject.Common". It contains the following code:
// define a simple factory interface
public interface IFactory
{
object CreateInstance();
}
// and a generic one (hey, why not?)
public interface IFactory<T> : IFactory
{
new T CreateInstance();
}
// define a Factory attribute that will be used to identify the concrete implementation of a factory
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Interface | AttributeTargets.Parameter, AllowMultiple = false, Inherited = true)]
public class FactoryAttribute : Attribute
{
public Type FactoryType { get; set; }
public FactoryAttribute(Type factoryType)
{
this.FactoryType = factoryType;
}
}
The IFactory interfaces and the Factory attribute will be known and understood by the other projects in my solution, since they both reference the MyProject.Common project.
Below is the code contained within my "MyProject.Components" project:
public class Test
{
public string Name { get; set; }
public string Type { get; set; }
public Test(string name, string type)
{
this.Name = name;
this.Type = type;
}
}
public class TestFactory : IFactory<Test>
{
#region IFactory<Test> Members
public Test CreateInstance()
{
return new Test("name", "type");
}
#endregion
#region IFactory Members
object IFactory.CreateInstance()
{
return this.CreateInstance();
}
#endregion
}
public class MyClass
{
// the Factory attribute on the first parameter indicates that the class TestFactory
// should be used as a factory object to construct the argument for this method
public string MyFunction([Factory(typeof(TestFactory))]Test obj)
{
if (obj == null)
return null;
else
return obj.ToString();
}
}
Finally, I've replaced the original reflection code that you posted with the following:
Assembly assembly = Assembly.Load("MyProject.Components");
Type dllType = assembly.GetType("MynameSpace.MyClass");
if (dllType != null)
{
MethodInfo m = dllType.GetMethod("MyFunction");
object objdll;
objdll = Activator.CreateInstance(dllType);
// use the parameter information to construct the arguments
ParameterInfo[] parameters = m.GetParameters();
object[] args;
if (parameters != null && parameters.Length > 0)
{
args = new object[parameters.Length];
for (int i = 0; i < parameters.Length; i++)
{
// check for factory attributes on the actual parameter
FactoryAttribute[] attributes = parameters[i].GetCustomAttributes(typeof(FactoryAttribute), true) as FactoryAttribute[];
// if no attributes were found, check the parameter type for factory attributes
if (attributes == null || attributes.Length == 0)
attributes = parameters[i].ParameterType.GetCustomAttributes(typeof(FactoryAttribute), true) as FactoryAttribute[];
// if no attributes were found still, then give up
if (attributes == null || attributes.Length == 0)
{
// this parameter has no factory specified,
// so how would this code know how to create the argument for that parameter ???
args[i] = null;
continue; // move on to the next parameter
}
// there should only be one factory attribute, so use the first one
// assumption made here is that all factory classes will have a parameterless constructor
IFactory factory = Activator.CreateInstance(attributes[0].FactoryType) as IFactory;
args[i] = factory.CreateInstance();
}
}
else
// there are no parameters
args = null;
if ((m != null))
{
strReturnValue += (string)m.Invoke(objdll, args);
}
}
If you mean by class object an object that is a Type object then you can just pass the type of the object as the parameter...e.g.
object[] args = {typeof(typeneeded)};
or
object[] args = { assembly.GetType(typeneeded) };
MethodInfo.Invoke() is declared as the following:
public Object Invoke(
Object obj,
Object[] parameters
)
The first parameter specifies the object to work on, the second one specifies the parameters for the function.
I replicated your code in LINQPad, and this works fine:
void Main()
{
string strReturnValue = "";
Test objTest = new Test("name","type");
Type dllType = typeof(MyClass);
if (dllType != null)
{
MethodInfo m = dllType.GetMethod("MyFunction");
object objdll;
objdll = Activator.CreateInstance(dllType);
object[] args = { objTest };
if ((m != null))
{
strReturnValue += (string)m.Invoke(objdll, args);
}
}
}
public class Test
{
public Test(string s1, string s2)
{
}
}
public class MyClass
{
public string MyFunction(Test t)
{
return "";
}
}
You will have to load the Test object the same way you load the MyClass instance, and as Test requires parameters in the constructor, you'll have to use a ConstructorInfo:
Assembly assembly = Assembly.Load(); //assembly of "Test"
Type testType = assembly.GetType("Test");
ConstructorInfo ci = testType.GetConstructor(
BindingFlags.Public | BindingFlags.Instance,
null,
new Type[]{typeof(string), typeof(string)},
null);
Test objTest = ci.Invoke(new object[] { "name", "type" });
now you can use objTest in the code.
I have a class Bar like this:
class Foo : IFoo {
[Range(0,255)]
public int? FooProp {get; set}
}
class Bar : IFoo
{
private Foo foo = new Foo();
public int? FooProp { get { return foo.FooProp; }
set { foo.FooProp= value; } }
}
I need to find the attribute [Range(0,255)] reflecting ONLY on the property Bar.FooProp. I mean, the prop is decorated in the class instance (.. new Foo()) not in the class when I am currently parsing. Infact Bar.FooProp has no attributes
EDIT
I moved attributes on the interface's definition, so what I have to do is parsing the inherited interfaces to find them. I can do that because Bar class must implement IFoo.In this particular case, I'm lucky, but the problem remains when I have no interfaces... I will take note for the next time
foreach(PropertyInfo property in properties)
{
IList<Type> interfaces = property.ReflectedType.GetInterfaces();
IList<CustomAttributeData> attrList;
foreach(Type anInterface in interfaces)
{
IList<PropertyInfo> props = anInterface.GetProperties();
foreach(PropertyInfo prop in props)
{
if(prop.Name.Equals(property.Name))
{
attrList = CustomAttributeData.GetCustomAttributes(prop);
attributes = new StringBuilder();
foreach(CustomAttributeData attrData in attrList)
{
attributes.AppendFormat(ATTR_FORMAT,
GetCustomAttributeFromType(prop));
}
}
}
}
I had a similar situation a while back where I had an attribute declared on a method in an interface, and I wanted to get the attribute from a method on a type implementing the interface. For example:
interface I {
[MyAttribute]
void Method( );
}
class C : I {
void Method( ) { }
}
The code below is used to check all of the interface implemented by the type, see which interface members the given method implements (using GetInterfaceMap), and returns any attributes on those members. Right before this, I also check if the attribute is present on the method itself.
IEnumerable<MyAttribute> interfaceAttributes =
from i in method.DeclaringType.GetInterfaces( )
let map = method.DeclaringType.GetInterfaceMap( i )
let index = GetMethodIndex( map.TargetMethods, method )
where index >= 0
let interfaceMethod = map.InterfaceMethods[index]
from attribute in interfaceMethod.GetCustomAttributes<MyAttribute>( true )
select attribute;
...
static int GetMethodIndex( MethodInfo[] targetMethods, MethodInfo method ) {
return targetMethods.IndexOf( target =>
target.Name == method.Name
&& target.DeclaringType == method.DeclaringType
&& target.ReturnType == method.ReturnType
&& target.GetParameters( ).SequenceEqual( method.GetParameters( ), PIC )
);
}
When looking at FooProp, there is nothing to identify the existence of a Foo (at any point). Perhaps you could add an attribute to identify the foo field, and reflect on that (via FieldInfo.FieldType)?