C# typeof() and Generics

J

Jorge Varas

Let's say that you have:

class A
{
T DoSomething<T>() where T : class, new()
{
T ThisObj = new T();
// Do something usefull...
return ThisObj;
}

void StartHere(Type type)
{
// And here is the problem
DoSomething<type>();
}
}

Any Ideas on how to call a generic function like 'DoSomething' when the calling function got the type as a parameter, not as a type parameter?

Jorge Varas
 
D

Dustin Campbell

Let's say that you have:
class A
{
T DoSomething<T>() where T : class, new()
{
T ThisObj = new T();
// Do something usefull...
return ThisObj;
}
void StartHere(Type type)
{
// And here is the problem
DoSomething<type>();
}
}
Any Ideas on how to call a generic function like 'DoSomething' when
the calling function got the type as a parameter, not as a type
parameter?

Unfortunately, this can't be done (and I don't see how "typeof" is used here).
The closest that you could get is to make the StartHere method generic like
this:

class A
{
T DoSomething<T>() where T : class, new()
{
T ThisObj = new T();
// Do something usefull...
return ThisObj;
}

void StartHere<T>()
{
// And here is the problem
T myValue = DoSomething<T>();
}
}

Best Regards,
Dustin Campbell
Developer Express Inc.
 
J

Jorge Varas

Dustin Campbell said:
Unfortunately, this can't be done (and I don't see how "typeof" is used here).

About the usage of typeof, here is an example of what I mean (this works):

class Program
{
static void Main(string[] args)
{
Test test = new Test();
test.CallMe();
}
}

class Test
{
public void CallMe()
{
this.DoSomething<Test>();
}
public void DoSomething<T>() where T : class, new()
{
Console.WriteLine(typeof(T).FullName);
}
}

Here I can use typeof to get the type of a Type Parameter. I was hoping that there was a way (maybe using typeof) to solve the first problem.
The closest that you could get is to make the StartHere method generic like
this:

class A
{
T DoSomething<T>() where T : class, new()
{
T ThisObj = new T();
// Do something usefull...
return ThisObj;
}

void StartHere<T>()
{
// And here is the problem
T myValue = DoSomething<T>();
}
}

I know, but in my case I am retrieving the type of a property using reflection, and that is the type that I need to pass to the generic DoSomething, so no point to make the initial call generic. The other option is to make a non-generic version of DoSomething and just eat the performance impact of the boxing.

Jorge Varas
 
M

Marc Gravell

You can, but you need to use reflection, which is (relatively) slow; in
particular, you would obtain the MethodInfo for the method ("GetMethod"),
and then call MakeGenericMethod(), which accepts the Type instances.

You would then .Invoke the returned method info.

Marc
 
D

Dustin Campbell

I know, but in my case I am retrieving the type of a property using
reflection, and that is the type that I need to pass to the generic
DoSomething, so no point to make the initial call generic. The other
option is to make a non-generic version of DoSomething and just eat
the performance impact of the boxing.

If you're already using reflection, boxing should be the least of your performance
worries. :) Second of all, the generic constraints that you've applied to
DoSomething<T> ensure that their won't be any boxing operations because only
reference types can be used with it. And finally, if you're using reflection,
you *could* use reflection to dynamically set the generic argument to your
method and invoke it like this:

using System;
using System.Reflection;

namespace ConsoleApp
{
class Program
{
class A
{
T DoSomething<T>() where T: class, new()
{
T ThisObj = new T();
// Do something usefull...
return ThisObj;
}

public void StartHere(Type type)
{
MethodInfo method = this.GetType().GetMethod("DoSomething", BindingFlags.NonPublic
| BindingFlags.Instance);
MethodInfo closedMethod = method.MakeGenericMethod(type);

object o = closedMethod.Invoke(this, null);
}
}

static void Main(string[] args)
{
new A().StartHere(typeof(A));
}
}
}

Of course, if the "class" generic constraint isn't used, there would potentially
be boxing because the Invoke() method returns a System.Object.

Best Regards,
Dustin Campbell
Developer Express Inc.
 
J

Jorge Varas

Cool!

I was not aware of MakeGenericMethodt. I'll give it a try.

{ ... things I have to do because C# has a very limited support for AOP }
 
J

Jorge Varas

I am caching the reflection information, but is still in reflected types
format (propertyInfo and so) so my performace is prety good (or my pc is
pretty fast, whichever is working) because I cache the information on
program start up, rather than at every call.

The boxing operation that I was worry about was going to be if I needed to
make a non-generic version (thus object type based) version of the same
function.

As I pointed out to mark, I didn't know about the "MakeGenericMethod" usage,
so I'll give it a try.

Dustin Campbell said:
I know, but in my case I am retrieving the type of a property using
reflection, and that is the type that I need to pass to the generic
DoSomething, so no point to make the initial call generic. The other
option is to make a non-generic version of DoSomething and just eat
the performance impact of the boxing.

If you're already using reflection, boxing should be the least of your
performance worries. :) Second of all, the generic constraints that
you've applied to DoSomething<T> ensure that their won't be any boxing
operations because only reference types can be used with it. And finally,
if you're using reflection, you *could* use reflection to dynamically set
the generic argument to your method and invoke it like this:

using System;
using System.Reflection;

namespace ConsoleApp
{
class Program
{
class A
{
T DoSomething<T>() where T: class, new()
{
T ThisObj = new T();
// Do something usefull...
return ThisObj;
}

public void StartHere(Type type)
{
MethodInfo method = this.GetType().GetMethod("DoSomething",
BindingFlags.NonPublic | BindingFlags.Instance);
MethodInfo closedMethod = method.MakeGenericMethod(type);

object o = closedMethod.Invoke(this, null);
}
}

static void Main(string[] args)
{
new A().StartHere(typeof(A));
}
}
}

Of course, if the "class" generic constraint isn't used, there would
potentially be boxing because the Invoke() method returns a System.Object.

Best Regards,
Dustin Campbell
Developer Express Inc.
 
D

Dustin Campbell

The boxing operation that I was worry about was going to be if I
needed to make a non-generic version (thus object type based) version
of the same function.

Sure, but I assumed that you weren't going to be using value types since
you had declared the "class" generic constraint. If you have an object-type
parameter but never use a value type with it, you won't have boxing.

Best Regards,
Dustin Campbell
Developer Express Inc
 
J

Jorge Varas

Then there is something more I need to learn... What I understand from what
you are saying is that there never is boxing between reference types? only
for value types that are used as objects?

Thus this piece of code does not do any boxing on the third line?

StringBuilder sb = new StringBuilder();
sb.Add("Whatever");
object o = (object)sb;
Console.WriteLine(o.ToString());

Jorge Varas
 
D

Dustin Campbell

Then there is something more I need to learn... What I understand from
what you are saying is that there never is boxing between reference
types? only for value types that are used as objects?

Thus this piece of code does not do any boxing on the third line?

StringBuilder sb = new StringBuilder();
sb.Add("Whatever");
object o = (object)sb;
Console.WriteLine(o.ToString());

Correct.

This code:

private void Test()
{
StringBuilder sb = new StringBuilder();
sb.Append("Whatever");
object o = (object)sb;
Console.WriteLine(o.ToString());
}

Compiles to this:

..method private hidebysig instance void Test() cil managed
{
.maxstack 2
.locals init (
[0] [mscorlib]System.Text.StringBuilder sb,
[1] object o)
L_0000: nop
L_0001: newobj instance void [mscorlib]System.Text.StringBuilder::.ctor()
L_0006: stloc.0
L_0007: ldloc.0
L_0008: ldstr "Whatever"
L_000d: callvirt instance [mscorlib]System.Text.StringBuilder [mscorlib]System.Text.StringBuilder::Append(string)
L_0012: pop
L_0013: ldloc.0
L_0014: stloc.1
L_0015: ldloc.1
L_0016: callvirt instance string object::ToString()
L_001b: call void [mscorlib]System.Console::WriteLine(string)
L_0020: nop
L_0021: ret
}

This code:

private void Test()
{
int i = 1;
object o = (object)i;
Console.WriteLine(o.ToString());
}

Compiles to this:

..method private hidebysig instance void Test() cil managed
{
.maxstack 1
.locals init (
[0] int32 i,
[1] object o)
L_0000: nop
L_0001: ldc.i4.1
L_0002: stloc.0
L_0003: ldloc.0
L_0004: box int32
L_0009: stloc.1
L_000a: ldloc.1
L_000b: callvirt instance string object::ToString()
L_0010: call void [mscorlib]System.Console::WriteLine(string)
L_0015: nop
L_0016: ret
}

Notice the boxing operation in the second method when the Int32 is cast to
an object.




Best Regards,
Dustin Campbell
Developer Express Inc.
 
J

Jorge Varas

Thanks for the explanation and your time.

One of these days I need to learn CLI, Emit() and all that area of .Net.
(Can you suggest a good starting point?)

All the hoops and loops that I am doing is to be able to intercept access to
properties of a class. I know that this can be done by inhert the class from
ContextBound and intercepting the call sink. But .Net supports only single
inheritance, which prevents me from using inheritance in my business model
if I also have to inherit from ContextBound.

I know about this project:

http://wwwse.fhs-hagenberg.ac.at/se/berufspraktika/2002/se99047/contents/english/aop_net.html

But I have not see a single example of it. Do you know more references?

Jorge Varas



Dustin Campbell said:
Then there is something more I need to learn... What I understand from
what you are saying is that there never is boxing between reference
types? only for value types that are used as objects?

Thus this piece of code does not do any boxing on the third line?

StringBuilder sb = new StringBuilder();
sb.Add("Whatever");
object o = (object)sb;
Console.WriteLine(o.ToString());

Correct.

This code:

private void Test()
{
StringBuilder sb = new StringBuilder();
sb.Append("Whatever");
object o = (object)sb;
Console.WriteLine(o.ToString());
}

Compiles to this:

.method private hidebysig instance void Test() cil managed
{
.maxstack 2
.locals init (
[0] [mscorlib]System.Text.StringBuilder sb,
[1] object o)
L_0000: nop L_0001: newobj instance void
[mscorlib]System.Text.StringBuilder::.ctor()
L_0006: stloc.0 L_0007: ldloc.0 L_0008: ldstr "Whatever"
L_000d: callvirt instance [mscorlib]System.Text.StringBuilder
[mscorlib]System.Text.StringBuilder::Append(string)
L_0012: pop L_0013: ldloc.0 L_0014: stloc.1 L_0015: ldloc.1 L_0016:
callvirt instance string object::ToString()
L_001b: call void [mscorlib]System.Console::WriteLine(string)
L_0020: nop L_0021: ret }

This code:

private void Test()
{
int i = 1;
object o = (object)i;
Console.WriteLine(o.ToString());
}

Compiles to this:

.method private hidebysig instance void Test() cil managed
{
.maxstack 1
.locals init (
[0] int32 i,
[1] object o)
L_0000: nop L_0001: ldc.i4.1 L_0002: stloc.0 L_0003: ldloc.0 L_0004:
box int32
L_0009: stloc.1 L_000a: ldloc.1 L_000b: callvirt instance string
object::ToString()
L_0010: call void [mscorlib]System.Console::WriteLine(string)
L_0015: nop L_0016: ret }

Notice the boxing operation in the second method when the Int32 is cast to
an object.




Best Regards,
Dustin Campbell
Developer Express Inc.
 
M

Marc Gravell

It isn't just obtaining the PropertyInfo, MethodInfo (etc) instances that is
slow; it is also (comparatively) slow when *using* them - e.g. GetValue /
SetValue for properties, Invoke for members... all slower, as it has to do a
lot to enusure you aren't attempting bad things. Quite painful if you are in
a tight loop.

Sometimes reflection is necessary; but in a lot of cases it is [ab]used
because it gets the programmer out of a hole. Often, suitable interface
declarations can be used to make this a *lot* quicker. Alternatively,
(typed) delegates can sometimes be suitable (especially in 2.0 where they
are quicker).

I don't know enough about your other code to know if it is being used wisely
(not do I really want to know ;-p) - I'm just making a generalisation.
Saying that; if you're code works, it may be inadvisable to hack it too
much... but worth considering for the future.

Marc
 
J

Jorge Varas

I am not in such tight loop. It is not a high performance app where every
milli-second count. So reflection performance is ok.
I am tinkering with my own code, not someone elses, so I can hack it as much
as I want. I don;t want to tie to specific interfaces or inherit from
certain class. I just want to decorate with attributes and the code being
intercepted, but looks like there is no way out of using reflection, or
inherit from ContextBoundObject.

The only project that did something in this style was:
http://wwwse.fhs-hagenberg.ac.at/se/berufspraktika/2002/se99047/contents/english/aop_net.html

But the guy will not give a sample.

Is there any recomended place to start studing the Emit codes and the
CLI/CLR?

Jorge Varas
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top