.NET 2.0: Invoking Methods Using Reflection And Generics Causes Exception - c#

I'm new to Stack Overflow, so forgive me. I've just started transititoning over to C# and I am stuck on a problem.
I am wanting to pass a generic class in and call a method from that class. So, my code looks as such:
public void UpdateRecords<T>(T sender) where T : new() //where T: new() came from Resharper
{
Type foo = Type.GetType(sender.GetType().ToString());
object[] userParameters = new object[2];
userParameters[0] = x;
userParameters[1] = y;
sender = new T(); //This was to see if I could solve my exception problem
MethodInfo populateRecord = foo.GetMethod("MethodInOtherClass");
populateMethod.Invoke(sender, userParameters);
}
Exception thrown: "Object reference not set to an instance of an object."
Again, I really apologize, as I am nearly brand new to C# and this is the first time I've ever handled reflection and generics. Thank you!

First of all, I would recommend running this code in the debugger and turning one "Break on Exception" to help isolate which line causes the error. This is a useful debugging technique that can help you find these types of problems more quickly in the future. Go to Debug >> Exceptions in VS and check the checkbox for Common Language Runtime Exceptions in the Thrown column.
Now for your issue. It's likely that sender is passed in as null. If so, the line:
Type foo = Type.GetType(sender.GetType().ToString());
will throw a NullReferenceException. Instead, you can use:
Type foo = typeof(T);
which identifies the type of the generic paramter without requiring an instance of it.
Now, without knowing more about what your code is trying to do, it's impossible to say whether instantiating an instance of T is the right thing to do. Just because ReSharper recommends adding where T : new() doesn't mean it's appropriate - unless you know that that's the right behavior.
Finally, I don't know if there is a compelling reason for using reflection to invoke MethodInOtherClass - perhaps there is. But since you're new to C#, I'll mention that if the type T will always be a subclass of some base type A or will always implement some interface I that includes the method you want to call, you can simply apply a generic constraint to let the compiler know this. Then you can call the method without reverting to using reflection:
public void UpdateRecords<T>(T sender)
where T : SomeBaseClass_Or_SomeInterface_ThatDefinesMethod
{
sender = new T();
sender.MethodInOtherClass( x, y );
}
Much nicer.
One final comment. It's unusual to pass in an argument to a method, and then ignore it completely - only to instantiate an instance within the method. There are cases when it's appropriate - but I tend to view it as a code smell. If possible, I would try to either get rid of the sender argument, or change the code to first test it for null and instantiate only then.

sender.GetType().ToString() returns the type name without the assembly name.
Type.GetType expects a type name with an assembly name (unless the type is in the executing assembly or mscorlib). If the type cannot be found (e.g., due to the missing assembly name), it returns a null reference.
Try changing your code to
Type foo = sender.GetType();
or even just
Type foo = typeof(T);

You should just be able to do this to get the type:
Type foo = typeof(T);
You didn't specify where you're getting the NullReferenceException, but I'm wondering if foo is coming back as null...

Either:
sender is null, so sender.GetType() will fail.
foo.GetMethod("MethodInOtherClass") returns null, so populateMethod.Invoke() will fail.
MethodInOtherClass depends on certain preconditions (non-null references), so it will fail when those are absent.

Related

Object type is same as type in inherited class [duplicate]

C# newbie question here. The following code (taken from the book "C# From Novice to Professional" by Christian Gross, Apress) gives an error:
worksheet.Add("C3", CellFactories.DoAdd(worksheet["A2"], worksheet["B1"]));
The reason is that the method DoAdd() does not accept the given arguments.
public static Func<object> DoAdd(Func<object> cell1, Func<object> cell2) {...}
VS claims that both args in the method call above are of type object whereas the method accepts only Func<object>. But the value of both worksheet elements is of type Func<object>:
worksheet.Add("A2", CellFactories.Static(10.0));
where this Static method just returns the given value:
public static Func<object> Static(object value) { return () => value; }
// return type= Func<object>
When I cast worksheet["A2"] as Func<object>, the code does work.
But there is something I don't understand. The type of the object instance is Func<object>. I have used the GetType() method to see proof of this, and compare the object types of the original elements to that of the cast object (which IS accepted):
Console.Writeline(worksheet["A2"].GetType());
// now cast to the correct type (why can't it do that implicitly, btw?)
Funk1 = worksheet["A2"] as Func<object>;
Console.Writeline(Funk1.GetType());
.. and they are ALL identical! (Type = System.Func'1[System.Object])
And even when I use the .Equals() method to compare both types, it returns true.
Yet, VS sees the first object instance as type object in the method call. Why? Why does the called method 'see' the argument as a different type than the GetType() returns?
(and if so, what good is the GetType() method?)
Thanks a lot for your advice/comments! (It's kinda hard to learn the language if the book examples give an error and you don't see the reason - hence, got the vague impression that something is wrong either with GetType() or VS.)
You need to understand the difference between dynamic typing and static typing. The indexer for your worksheet object most likely has a static type of object.
public object this[string cell]{get{...}set{...}}
Because all objects in C# inherit from type object, the object reference stored in a cell can be a reference to any object.
That is, because a delegate (such as Func<T>) is an object, it can be stored in an object reference:
Func<object> func = ()=>return "foo";
object o = func; // this compiles fine
And the compiler can figure this all out, because it understands implicitly that a derived class can be stored in a reference to a base class.
What the compiler cannot do automatically, is determine what the dynamic type, or run time type of an object is.
Func<object> func = ()=>return "foo";
object o = func; // this compiles fine
func = o; // <-- ERROR
The compiler doesn't know that the object stored in o is actually of type Func<object>. It's not supposed to keep track of this. This is information that must be checked at run time.
func = (Func<object>)o; // ok!
The above line of code compiles into something that behaves similarly to this:
if(o == null)
func = null;
else if(typeof(Func<object>).IsAssignableFrom(func.GetType()))
__copy_reference_address__(func, o); // made up function! demonstration only
else throw new InvalidCastException();
In this way, any cast (conversion from one type to another) can be checked at run time to make sure it's valid and safe.
Others have given accurate and detailed answers, but here I will try to explain in simple language.
When you write worksheet["A2"] you really are calling a member function of worksheet
worksheet has a member function named [] that accepts a string and returns an object
The signature of the member function [] looks like object this[string id]
So the function worksheet["A2"] returns something that is an object. It could be an int or a string or many other things. All the compiler knows is that it will be an object.
In the example, you have it returning a Func<object>. This is fine, because Func<object> is an object. However, you then pass the result of that function in as a parameter to another function.
The problem here is that the compiler only knows that worksheet["A2"] returns an object. That is as specific as the compiler can be.
So the compiler sees that worksheet["A2"] is an object, and you are trying to pass the object to a function that does not accept object as a parameter.
So here you have to tell the compiler "hey dummy, that's a Func<object>" by casting the returned object to the correct type.
worksheet.Add("C3", CellFactories.DoAdd(worksheet["A2"], worksheet["B1"]));
can be re-written as
worksheet.Add("C3", CellFactories.DoAdd((Func<object>)worksheet["A2"], (Func<object>)worksheet["B1"]));
Now the compiler knows that, even though the [] function returns an object, it can treat it like a Func<object>.
side note: You're probably doing too much on one line. That may be hard for people to read in the future.
Why does the called method 'see' the argument as a different type than the GetType() returns?
The compiler only knows that worksheet[] returns an object. The compiler can not call GetType() on it at compile time.
What good is the GetType() method?
There are quite a few uses and abuses of the GetType() method, but that is an entirely different discussion. ;)
In summary, the compiler does not assume anything about types. This is a good thing because you get a compile time error when you try to fit this square peg into a round hole. If the compiler did not complain, this error would surface at run-time, which means you would probably need a unit test to detect the problem.
You can get around this problem by telling the compiler "I know for a fact that this thing is a round peg, trust me." and then it will compile.
If you lie to the compiler, you will get a run-time error when that code is executed.
This is called "static typing". The opposing philosophy is called "dynamic typing" where type checks are done at run-time. Static vs dynamic is a lengthy debate and you should probably research it on your own if you're interested.
VS claims that both args in the method call above are of type object whereas the method accepts only Func. But the value of both worksheet elements is of type Func
Yes, but the declared type is object. The compiler can't know that the actual runtime type will be Func<object>, so an explicit cast is necessary.

Can C# return a type in a method?

can i have something like:
AMess.Foo = MessyData.GetData<MessyData.GetType(key)>(key);
?
my problem is similar to my example. I have a data source is dynamically typed, so i have to do something to determine the data type. but it's a pain in the ass to put type check everywhere. so i think maybe i could have some code just to tell me what type it is. can i do this?
or maybe better solution?
please not worry about AMess.Foo, it eats everything.
No, it can't. You'll have to use Reflection instead. I don't have Visual Studio installed on my machine at the moment so I can't test the syntax at the moment, but this should be close:
var type = MessyData.GetType(key);
var castMethod = MessyData.GetType().GetMethod("GetData").MakeGenericMethod(type);
AMess.Foo = castMethod.Invoke(MessyData, new[] { key });
No. Generic type arguments must be resolved at compile-time.
If you use:
MessyData.GetData<SomeType>(key);
then SomeType needs to be the name of a specific class (or interface, struct, etc.). It can never be an expression like in your question.
If you let the type be inferred, as in:
MessyData.GetData(key); // infer type arg
then the type used in the call is the compile-time type of the key variable, not the run-time type. These types can differ.
If you really want dynamic typing, you can have GetData take in a parameter of type dynamic (the method will be non-generic). But what is the problem you are trying to solve?

Why calling ISet<dynamic>.Contains() compiles, but throws an exception at runtime?

Please, help me to explain the following behavior:
dynamic d = 1;
ISet<dynamic> s = new HashSet<dynamic>();
s.Contains(d);
The code compiles with no errors/warnings, but at the last line I get the following exception:
Unhandled Exception: Microsoft.CSharp.RuntimeBinder.RuntimeBinderException: 'System.Collections.Generic.ISet<object>' does not contain a definition for 'Contains'
at CallSite.Target(Closure , CallSite , ISet`1 , Object )
at System.Dynamic.UpdateDelegates.UpdateAndExecuteVoid2[T0,T1](CallSite site, T0 arg0, T1 arg1)
at FormulaToSimulation.Program.Main(String[] args) in
As far as I can tell, this is related to dynamic overload resolution, but the strange things are
(1) If the type of s is HashSet<dynamic>, no exception occurs.
(2) If I use a non-generic interface with a method accepting a dynamic argument, no exception occurs.
Thus, it looks like this problem is related particularly with generic interfaces, but I could not find out what exactly causes the problem.
Is it a bug in the compiler/typesystem, or legitimate behavior?
The answers you have received so far do not explain the behaviour you are seeing. The DLR should find the method ICollection<object>.Contains(object) and call it with the boxed integer as a parameter, even if the static type of the variable is ISet<dynamic> instead of ICollection<dynamic> (because the former derives from the latter).
Therefore, I believe this is a bug and I have reported it to Microsoft Connect. If it turns out that the behaviour is somehow desirable, they will post a comment to that effect there.
Why it compiles: the entire expression is evaluated as dynamic (hover your mouse over it inside your IDE to confirm), which means that it is a runtime check.
Why it bombs: My (completely wrong, see below) guess is that it is because you cannot implement a dynamic interface in such a manner. For example, the compiler does not allow you to create a class that implements ISet<dynamic>, IEnumerable<dynamic>, IList<dynamic>, etc. You get a compile-time error stating "cannot implement a dynamic interface". See Chris Burrows' blog post on this subject.
http://blogs.msdn.com/b/cburrows/archive/2009/02/04/c-dynamic-part-vii.aspx
However, since it's hitting the DLR anyway, you can make s completely dynamic.
dynamic s = new HashSet<dynamic>;
s.Contains(d);
Compiles and runs.
Edit: the second part of this answer is completely wrong. Well, it is correct in that you can't implement such an interface as ISet<dynamic>, but that's not why this blows up.
See Julian's answer below. You can get the following code to compile and run:
ICollection<dynamic> s = new HashSet<dynamic>();
s.Contains(d);
The Contains method is defined on ICollection<T>, not ISet<T>. The CLR doesn't allow an interface base method to be called from a derived interface. You usually doesn't see this with static resolution because the C# compiler is smart enough to emit a call to ICollection<T>.Contains, not the non-existing ISet<T>.Contains.
Edit: The DLR mimics the CLR behavior, that's why you get the exception. Your dynamic call is done on an ISet<T>, not an HashSet<T> the DLR will mimics the CLR: for an interface, only interfaces methods are searched for, not base interfaces (contrary to classes where this behavior is present).
For an in-depth explanation, see a previous response of mine to a similar question:
Strange behaviour when using dynamic types as method parameters
Note that the type dynamic doesn’t actually exist at run-time. Variables of that type are actually compiled into variables of type object, but the compiler turns all the method calls (and properties and everything) that involve such an object (either as the this object or as a parameter) into a call that is resolved dynamically at runtime (using System.Runtime.CompilerServices.CallSiteBinder and related magic).
So what happens in your case is that the compiler:
turns ISet<dynamic> into ISet<object>;
turns HashSet<dynamic> into HashSet<object>, which becomes the actual run-time type of the instance you’re storing in s.
Now if you try to invoke, say,
s.Contains(1);
this actually succeeds without a dynamic invocation: it really just calls ISet<object>.Contains(object) on the boxed integer 1.
But if you try to invoke
s.Contains(d);
where d is dynamic, then the compiler turns the statement into one that determines, at runtime, the correct overload of Contains to call based on the runtime type of d. Perhaps now you can see the problem:
The compiler emits code that definitely searches the type ISet<object>.
That code determines that the dynamic variable has type int at runtime and tries to find a method Contains(int).
ISet<object> does not contain a method Contains(int), hence the exception.
ISet interface does not have a method 'Contains', HashSet does however?
EDIT
What i meant to say was the binder resolves 'Contains' when given the HashSet concreate type, but doesnt find the inherited 'Contains' method in the interface...

Activator.CreateInstance(Type) for a type without parameterless constructor [duplicate]

This question already has answers here:
Creating instance of type without default constructor in C# using reflection
(4 answers)
Closed 9 years ago.
Reading existing code at work, I wondered how come this could work. I have a class defined in an assembly :
[Serializable]
public class A
{
private readonly string _name;
private A(string name)
{
_name = name;
}
}
And in another assembly :
public void f(Type t) {
object o = Activator.CreateInstance(t);
}
and that simple call f(typeof(A))
I expected an exception about the lack of a parameterless constructor because AFAIK, if a ctor is declared, the compiler isn't supposed to generate the default public parameterless constructor.
This code runs under .NET 2.0.
[EDIT] I'm sorry but I misread the actual code... The sample I provided doesn't illustrate it. I accepted JonH answer because it provided a good piece of information.
An alternative is:
object obj = System.Runtime.Serialization.FormatterServices
.GetUninitializedObject(t);
which creates the object in memory but doesn't run any constructor. Scary.
See this: Creating instance of type without default constructor in C# using reflection
Here's to the future also, this is in regards to C# 4.0:
Posted by Microsoft on 1/4/2010 at
2:08 PM
We have reviewed your bug and
have determined that the behavior that
you described is by design. We are now
archiving this issue. Thanks for using
Visual Studio and the .Net Framework!
There are many overloads of Activator.CreateInstance, the one that
takes a single type parameter only
invokes the default parameterless
constructor. Constructors that take an
optional parameter like the one in
your example are not default
constructors. To invoke them you need
to:
use an overload that takes an argument array
Pass in Type.Missing as the argument
Specify OptionalParamBinding in the BindingFlags
Here is an example:
Activator.CreateInstance(typeof(MyClassName),
BindingFlags.CreateInstance |
BindingFlags.Public |
BindingFlags.Instance |
BindingFlags.OptionalParamBinding,
null, new Object[] {Type.Missing}, null);
Thanks,
Weitao Su
Microsoft Corp.
This may not be feasible for you to do, but the simplest thing is to pass the argument list after the type like so:
Activator.CreateInstance(t, "name");
You'll want to think about what f() is really trying to do. Should it be instantiating an object of type A at all? What other classes will it instantiate?
One possibility is to have a switch statement within f() which passes the correct parameters to CreateInstance() for class A. This won't scale, but that may not be an issue for you.
There are two confusing flavors of CreateInstance: one that takes an object of type System.Type as a parameter, another that takes a type parameter T.
First one:
object Activator.CreateInstance(type As Type) { }
Second one:
T Activator.CreateInstance<T>() { }
The second one is the one that doesn't work for a class without a public parameterless constructor.
Note that it almost never makes sense to even use the second one; in any case where it would be used, it would be better to have a where T : new() constraint on your class and simply use T's constructor.
The MSDN documentation agrees:
In general, there is no use for the
CreateInstance in application code,
because the type must be known at
compile time. If the type is known at
compile time, normal instantiation
syntax can be used (new operator in
C#, New in Visual Basic, gcnew in
C++).
EDIT: I may have spoken too soon; it appears the first version isn't supposed to work either.

Method may only be called on a Type for which Type.IsGenericParameter is true

I am getting this error on a routine which uses reflection to dump some object properties, something like the code below.
MemberInfo[] members = obj.GetType().GetMembers(BindingFlags.Public | BindingFlags.Instance) ;
foreach (MemberInfo m in members)
{
PropertyInfo p = m as PropertyInfo;
if (p != null)
{
object po = p.GetValue(obj, null);
...
}
}
The actual error is "Exception has been thrown by the target of an invocation"
with an inner exception of "Method may only be called on a Type for which Type.IsGenericParameter is true."
At this stage in the debugger obj appears as
{Name = "SqlConnection" FullName = "System.Data.SqlClient.SqlConnection"}
with the type System.RuntimeType
The method m is {System.Reflection.MethodBase DeclaringMethod}
Note that obj is of type System.RuntimeType and members contains 188 items whereas a simple
typeof(System.Data.SqlClient.SqlConnection).GetMembers(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance) only returns 65.
I tried checking isGenericParameter on both obj and p.PropertyType, but this seems to be false for most properties including those where p.GetValue works.
So what exactly is a "Type for which Type.IsGenericParameter is true" and more importantly
how do I avoid this error without a try/catch?
So what exactly is a "Type for which Type.IsGenericParameter is true"
That means it is a generic type argument in an open generic type - i.e. where we haven't picked a T yet; for example:
// true
bool isGenParam = typeof(List<>).GetGenericArguments()[0].IsGenericParameter;
// false (T is System.Int32)
bool isGenParam = typeof(List<int>).GetGenericArguments()[0].IsGenericParameter;
So; have you got some open generics hanging around? Perhaps if you can give an example of where you got your obj from?
Firstly, you've made an incorrect assumption, that is, you've assumed that members has returned the members of an instance of System.Data.SqlClient.SqlConnection, which it has not. What has been returned are the members of an instance of System.Type.
From the MSDN documentation for DeclaringType:
Getting the DeclaringMethod property
on a type whose IsGenericParameter
property is false throws an
InvalidOperationException.
So... it's understandable that an InvalidOperationException is being thrown, since naturally, you're not dealing with an open generic type here. See Marc Gravell's answer for an explanation of open generic types.
All the clues are in there. The type of the obj is the Type class itself (or rather the strange RuntimeType derivative).
At the point of failure you loop has arrived the Type class property called DeclaringMethod. However the type that this instance of the Type class is describing is System.Data.SqlClient.SqlConnection which is not a Generic Type of a method.
Hence attempting to invoke the get on DeclaringMethod results in the exception.
The key is you are examining the type of the class Type. Its a bit circular but think of this:-
SqlConnection s = new SqlConnection();
Type t = s.GetType()
Type ouch = t.GetType()
What is class ouch describing?
How do I avoid this error without a try/catch?
You almost certainly can't. When you call p.GetValue, you're calling the getter on that property, which could throw any kind of exception. For example, SqlConnection.ServerVersion will throw an exception if the connection is closed, and you have to handle that.
Where are these extra members coming from?
Your obj already contains the RuntimeType object representing SqlConnection, rather than an instance of SqlConnection. obj.GetMembers() would return the 65 members of the SqlConnection class, but by calling GetType() again, you get the 188 members of RuntimeType.
What is IsGenericParameter?
Instead of representing a class, you can have an instance of RuntimeType that represents a generic parameter to a class or method (The T and TOutput in List<T>.ConvertAll<TOutput>. In this case, DeclaringMethod on the object representing TOutput would let you get a MethodInfo object representing the ConvertAll<> method. However, when the RuntimeType represents a class, the idea of a declaring method makes no sense. That's why reading the property causes the exception that you saw.
My problem was solved by deleting repetitive fields and tables in my model and commented defining query and deleted store: in Model.edmx XML File.

Categories