By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
435,491 Members | 3,230 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 435,491 IT Pros & Developers. It's quick & easy.

Structs and delegates

P: n/a
Below is a bit of code that creates a delegate.
The delegate invokes a member function of a struct.
The code compiles, but has surprising behavior:

using System;

namespace ConsoleApplication1
{
public struct SimpleStruct
{
public int i;

public void assignToI(int x)
{
Console.WriteLine("Assigning i: " + x);
i = x;
Console.WriteLine("i = " + x);
}
}

class Class1
{
public delegate void Assigner(int s);

private static void callDelegate(Assigner a, int i)
{
a(i);
}

static void Main(string[] args)
{
SimpleStruct s = new SimpleStruct();
s.assignToI(1); // Works as expected, assigns 1 to s.i
System.Console.WriteLine("s.i after calling assignToI = " + s.i);

// Create a delegate that calls assignToI()
Assigner assign_to_i = new Assigner(s.assignToI);

// Call helper function, passing delegate and new value for s.i
callDelegate(assign_to_i, 99);

System.Console.WriteLine("s.i after calling delegate = " + s.i);
// Assignment is lost, s.i still contains 1!!!
}
}
}

When run, this code prints:

$ ./ConsoleApplication1.exe
Assigning i: 1
i = 1
s.i after calling assignToI = 1
Assigning i: 99
i = 99
s.i after calling delegate = 1

Note that the direct call s.assignToI() works as expected:
after the call, the structure member has new value.

However, the call via the delagate does not work as expected:
the value that is assigned is lost, and the structure member
has the old value.

Now, I realize what is going on here. The delegate constructor
expects an argument of type object. In other words, the expression

new Assigner(s.assignToI)

is internally converted to something like

new Delegate(s, "assignToI")

So, the struct is silently boxed and, when the delegate runs,
what it assigns to is a temporary copy of the struct on the heap,
instead of assigning to the real struct.

Several thoughts here:

1) I find this surprising. At no point in my code have I passed the
structure as a parameter, so I don't expect this behavior. (Yes,
I know that the struct ends up being boxed, but that is not manifest
anywhere in the code.)

2) I believe that the behavior is wrong. (Yes, I understand *why* it
works the way it does, but that doesn't necessarily make the behavior
right.) Delegates are the equivalent of C++ member function pointers.
If I create a delegate and pass a method of a struct, I expect the
delegate to call the method on the struct instance I specified, not
some temporary copy.

3) Even if we accept that the behavior is correct, then why does the compiler
allow me to write this? After all, there is no way that such a delegate would
ever do something useful, so why not at least emit a warning?

4) The silent boxing and unboxing in C# seems to be more of a curse than helpful.
It is too easy to have a value type boxed, only to end up with invocations
made to a boxed copy on the heap.

Finally, I'm looking for suggestions as to how I can achieve what the above
code is trying to do. Basically, what I need to do is assign to a member of
an already instantiated structure, but without knowing the type of the structure.
That is, I want to, at least in spirit, be able to assign to a structure member
via a pointer to the structure.
(This issue arises in the context of unmarshaling data from the wire and, for
various legitimate reasons, I have to assign to a member of an already instatiated
structure, instead of instantiating the structure after I have all its member values.)

I tried using pointers and unsafe code, but that only works for types that are unmanaged.
However, the structure may contain managed members, in which case I can no
longer create a pointer to the struct, so this doesn't work either.

Any other ideas anyone?

Thanks,

Michi.
Nov 17 '05 #1
Share this Question
Share on Google+
16 Replies


P: n/a
If you make SimpleStruct a class, it will work as expected. The problem is
that a struct is a value type, not a reference.

Read more here:
http://msdn.microsoft.com/library/de...arpspec_11.asp

Pete

"Michi Henning" <mi***@zeroc.com> wrote in message
news:OU******************@TK2MSFTNGP09.phx.gbl...
Below is a bit of code that creates a delegate.
The delegate invokes a member function of a struct.
The code compiles, but has surprising behavior:

using System;

namespace ConsoleApplication1
{
public struct SimpleStruct
{
public int i;

public void assignToI(int x)
{
Console.WriteLine("Assigning i: " + x);
i = x;
Console.WriteLine("i = " + x);
}
}

class Class1
{
public delegate void Assigner(int s);

private static void callDelegate(Assigner a, int i)
{
a(i);
}

static void Main(string[] args)
{
SimpleStruct s = new SimpleStruct();
s.assignToI(1); // Works as expected, assigns 1 to s.i
System.Console.WriteLine("s.i after calling assignToI = " +
s.i);

// Create a delegate that calls assignToI()
Assigner assign_to_i = new Assigner(s.assignToI);

// Call helper function, passing delegate and new value for
s.i
callDelegate(assign_to_i, 99);

System.Console.WriteLine("s.i after calling delegate = " +
s.i);
// Assignment is lost, s.i still contains 1!!!
}
}
}

When run, this code prints:

$ ./ConsoleApplication1.exe
Assigning i: 1
i = 1
s.i after calling assignToI = 1
Assigning i: 99
i = 99
s.i after calling delegate = 1

Note that the direct call s.assignToI() works as expected:
after the call, the structure member has new value.

However, the call via the delagate does not work as expected:
the value that is assigned is lost, and the structure member
has the old value.

Now, I realize what is going on here. The delegate constructor
expects an argument of type object. In other words, the expression

new Assigner(s.assignToI)

is internally converted to something like

new Delegate(s, "assignToI")

So, the struct is silently boxed and, when the delegate runs,
what it assigns to is a temporary copy of the struct on the heap,
instead of assigning to the real struct.

Several thoughts here:

1) I find this surprising. At no point in my code have I passed the
structure as a parameter, so I don't expect this behavior. (Yes,
I know that the struct ends up being boxed, but that is not manifest
anywhere in the code.)

2) I believe that the behavior is wrong. (Yes, I understand *why* it
works the way it does, but that doesn't necessarily make the behavior
right.) Delegates are the equivalent of C++ member function pointers.
If I create a delegate and pass a method of a struct, I expect the
delegate to call the method on the struct instance I specified, not
some temporary copy.

3) Even if we accept that the behavior is correct, then why does the
compiler
allow me to write this? After all, there is no way that such a delegate
would
ever do something useful, so why not at least emit a warning?

4) The silent boxing and unboxing in C# seems to be more of a curse than
helpful.
It is too easy to have a value type boxed, only to end up with
invocations
made to a boxed copy on the heap.

Finally, I'm looking for suggestions as to how I can achieve what the
above
code is trying to do. Basically, what I need to do is assign to a member
of
an already instantiated structure, but without knowing the type of the
structure.
That is, I want to, at least in spirit, be able to assign to a structure
member
via a pointer to the structure.
(This issue arises in the context of unmarshaling data from the wire and,
for
various legitimate reasons, I have to assign to a member of an already
instatiated
structure, instead of instantiating the structure after I have all its
member values.)

I tried using pointers and unsafe code, but that only works for types that
are unmanaged.
However, the structure may contain managed members, in which case I can no
longer create a pointer to the struct, so this doesn't work either.

Any other ideas anyone?

Thanks,

Michi.

Nov 17 '05 #2

P: n/a
Pete Davis wrote:
If you make SimpleStruct a class, it will work as expected. The problem is
that a struct is a value type, not a reference.
Yes, I am aware of that. As I said in my post:
So, the struct is silently boxed and, when the delegate runs,
what it assigns to is a temporary copy of the struct on the heap,
instead of assigning to the real struct.

[...]

4) The silent boxing and unboxing in C# seems to be more of a curse than
helpful. It is too easy to have a value type boxed, only to end up with
invocations made to a boxed copy on the heap.


My point is that either it shouldn't behave as it does or, if it has to behave
as it does, the compiler should at least warn me that it's not going to work.

Michi.
Nov 17 '05 #3

P: n/a
Michi Henning <mi***@zeroc.com> wrote:
Below is a bit of code that creates a delegate.
The delegate invokes a member function of a struct.
The code compiles, but has surprising behavior:
<snip>
3) Even if we accept that the behavior is correct, then why does the compiler
allow me to write this? After all, there is no way that such a delegate would
ever do something useful, so why not at least emit a warning?


What do you expect it to do if the delegate "lives" for longer than the
current stack frame? What could it possibly operate on then?
Furthermore, how do you expect the delegate to maintain a reference to
the struct on the stack in the first place?

Note that this behaviour is well-defined in the spec - see section
14.5.10.3 of the ECMA spec.

--
Jon Skeet - <sk***@pobox.com>
http://www.pobox.com/~skeet Blog: http://www.msmvps.com/jon.skeet
If replying to the group, please do not mail me too
Nov 17 '05 #4

P: n/a
Hi,
if I compile your code under Rotor csc, it gives me the following error:
---------------------------------------
Unhandled Exception: System.NotSupportedException: Delegates on value
classes can only be formed on virtual methods
at System.Delegate.NeverCallThis(Object target, IntPtr slot)
at ConsoleApplication1.Class1.Main(String[] args)
---------------------------------------

Ab.
http://joehacker.blogspot.com

"Michi Henning" <mi***@zeroc.com> wrote in message
news:OU******************@TK2MSFTNGP09.phx.gbl...
Below is a bit of code that creates a delegate.
The delegate invokes a member function of a struct.
The code compiles, but has surprising behavior:

using System;

namespace ConsoleApplication1
{
public struct SimpleStruct
{
public int i;

public void assignToI(int x)
{
Console.WriteLine("Assigning i: " + x);
i = x;
Console.WriteLine("i = " + x);
}
}

class Class1
{
public delegate void Assigner(int s);

private static void callDelegate(Assigner a, int i)
{
a(i);
}

static void Main(string[] args)
{
SimpleStruct s = new SimpleStruct();
s.assignToI(1); // Works as expected, assigns 1 to s.i
System.Console.WriteLine("s.i after calling assignToI = " + s.i);
// Create a delegate that calls assignToI()
Assigner assign_to_i = new Assigner(s.assignToI);

// Call helper function, passing delegate and new value for s.i callDelegate(assign_to_i, 99);

System.Console.WriteLine("s.i after calling delegate = " + s.i); // Assignment is lost, s.i still contains 1!!!
}
}
}

When run, this code prints:

$ ./ConsoleApplication1.exe
Assigning i: 1
i = 1
s.i after calling assignToI = 1
Assigning i: 99
i = 99
s.i after calling delegate = 1

Note that the direct call s.assignToI() works as expected:
after the call, the structure member has new value.

However, the call via the delagate does not work as expected:
the value that is assigned is lost, and the structure member
has the old value.

Now, I realize what is going on here. The delegate constructor
expects an argument of type object. In other words, the expression

new Assigner(s.assignToI)

is internally converted to something like

new Delegate(s, "assignToI")

So, the struct is silently boxed and, when the delegate runs,
what it assigns to is a temporary copy of the struct on the heap,
instead of assigning to the real struct.

Several thoughts here:

1) I find this surprising. At no point in my code have I passed the
structure as a parameter, so I don't expect this behavior. (Yes,
I know that the struct ends up being boxed, but that is not manifest
anywhere in the code.)

2) I believe that the behavior is wrong. (Yes, I understand *why* it
works the way it does, but that doesn't necessarily make the behavior
right.) Delegates are the equivalent of C++ member function pointers.
If I create a delegate and pass a method of a struct, I expect the
delegate to call the method on the struct instance I specified, not
some temporary copy.

3) Even if we accept that the behavior is correct, then why does the compiler allow me to write this? After all, there is no way that such a delegate would ever do something useful, so why not at least emit a warning?

4) The silent boxing and unboxing in C# seems to be more of a curse than helpful. It is too easy to have a value type boxed, only to end up with invocations made to a boxed copy on the heap.

Finally, I'm looking for suggestions as to how I can achieve what the above code is trying to do. Basically, what I need to do is assign to a member of an already instantiated structure, but without knowing the type of the structure. That is, I want to, at least in spirit, be able to assign to a structure member via a pointer to the structure.
(This issue arises in the context of unmarshaling data from the wire and, for various legitimate reasons, I have to assign to a member of an already instatiated structure, instead of instantiating the structure after I have all its member values.)
I tried using pointers and unsafe code, but that only works for types that are unmanaged. However, the structure may contain managed members, in which case I can no
longer create a pointer to the struct, so this doesn't work either.

Any other ideas anyone?

Thanks,

Michi.

Nov 17 '05 #5

P: n/a
I meant compile AND run.

Ab.
"Abubakar" <ab*******@gmail.com> wrote in message
news:eR*************@TK2MSFTNGP10.phx.gbl...
Hi,
if I compile your code under Rotor csc, it gives me the following error:
---------------------------------------
Unhandled Exception: System.NotSupportedException: Delegates on value
classes can only be formed on virtual methods
at System.Delegate.NeverCallThis(Object target, IntPtr slot)
at ConsoleApplication1.Class1.Main(String[] args)
---------------------------------------

Ab.
http://joehacker.blogspot.com

"Michi Henning" <mi***@zeroc.com> wrote in message
news:OU******************@TK2MSFTNGP09.phx.gbl...
Below is a bit of code that creates a delegate.
The delegate invokes a member function of a struct.
The code compiles, but has surprising behavior:

using System;

namespace ConsoleApplication1
{
public struct SimpleStruct
{
public int i;

public void assignToI(int x)
{
Console.WriteLine("Assigning i: " + x);
i = x;
Console.WriteLine("i = " + x);
}
}

class Class1
{
public delegate void Assigner(int s);

private static void callDelegate(Assigner a, int i)
{
a(i);
}

static void Main(string[] args)
{
SimpleStruct s = new SimpleStruct();
s.assignToI(1); // Works as expected, assigns 1 to s.i
System.Console.WriteLine("s.i after calling assignToI = " + s.i);

// Create a delegate that calls assignToI()
Assigner assign_to_i = new Assigner(s.assignToI);

// Call helper function, passing delegate and new value for

s.i
callDelegate(assign_to_i, 99);

System.Console.WriteLine("s.i after calling delegate = " +

s.i);
// Assignment is lost, s.i still contains 1!!!
}
}
}

When run, this code prints:

$ ./ConsoleApplication1.exe
Assigning i: 1
i = 1
s.i after calling assignToI = 1
Assigning i: 99
i = 99
s.i after calling delegate = 1

Note that the direct call s.assignToI() works as expected:
after the call, the structure member has new value.

However, the call via the delagate does not work as expected:
the value that is assigned is lost, and the structure member
has the old value.

Now, I realize what is going on here. The delegate constructor
expects an argument of type object. In other words, the expression

new Assigner(s.assignToI)

is internally converted to something like

new Delegate(s, "assignToI")

So, the struct is silently boxed and, when the delegate runs,
what it assigns to is a temporary copy of the struct on the heap,
instead of assigning to the real struct.

Several thoughts here:

1) I find this surprising. At no point in my code have I passed the
structure as a parameter, so I don't expect this behavior. (Yes,
I know that the struct ends up being boxed, but that is not manifest
anywhere in the code.)

2) I believe that the behavior is wrong. (Yes, I understand *why* it
works the way it does, but that doesn't necessarily make the behavior right.) Delegates are the equivalent of C++ member function pointers. If I create a delegate and pass a method of a struct, I expect the
delegate to call the method on the struct instance I specified, not
some temporary copy.

3) Even if we accept that the behavior is correct, then why does the

compiler
allow me to write this? After all, there is no way that such a

delegate would
ever do something useful, so why not at least emit a warning?

4) The silent boxing and unboxing in C# seems to be more of a curse than

helpful.
It is too easy to have a value type boxed, only to end up with

invocations
made to a boxed copy on the heap.

Finally, I'm looking for suggestions as to how I can achieve what the

above
code is trying to do. Basically, what I need to do is assign to a member

of
an already instantiated structure, but without knowing the type of the

structure.
That is, I want to, at least in spirit, be able to assign to a structure

member
via a pointer to the structure.
(This issue arises in the context of unmarshaling data from the wire and, for
various legitimate reasons, I have to assign to a member of an already instatiated
structure, instead of instantiating the structure after I have all its

member values.)

I tried using pointers and unsafe code, but that only works for types

that are unmanaged.
However, the structure may contain managed members, in which case I can

no longer create a pointer to the struct, so this doesn't work either.

Any other ideas anyone?

Thanks,

Michi.


Nov 17 '05 #6

P: n/a
The difficulty, as I see it, is that you are using a screwdriver to
pound a nail into a wall.

Why do you have a mutable structure in the first place? Yes, I know
that Microsoft did it with Point, Rectangle, etc. (for specific
performance reasons), but that doesn't make it generally good practice,
because you end up with precisely the kinds of problems you're
outlining here.

I've built many structs, but they're all immutable, so they act just
like values, like int, double, etc., so I never run across these
problems.

Should it be easier to modify a struct field using something like a C++
method pointer? That sounds to me like the assertion that it really
should be easier to use a screwdriver as a hammer, and there's
something wrong with screwdrivers that doesn't permit them to be easily
used in that way.

I side with Pete Davis: What design problem caused you to choose a
struct over a class here?

Nov 17 '05 #7

P: n/a
Jon Skeet [C# MVP] wrote:


What do you expect it to do if the delegate "lives" for longer than the
current stack frame? What could it possibly operate on then?
When the delegate tries to call the struct member function, it could
throw an exception if the struct is no longer there.
Furthermore, how do you expect the delegate to maintain a reference to
the struct on the stack in the first place?
Well, that would be the job of the compiler.
Note that this behaviour is well-defined in the spec - see section
14.5.10.3 of the ECMA spec.


Right. No argument there. The behavior is exactly as prescribed by
the spec. That doesn't make it any more useful though ;-)

I honestly can't think of why I would want to deliberately do this--
having a delegate call into a temporary anonymous copy of a value
type would never achieve anything useful, as far as I can see.
It would be nice if the compiler emitted a warning for this case.

Cheers,

Michi.
Nov 17 '05 #8

P: n/a
Bruce Wood wrote:

Why do you have a mutable structure in the first place? Yes, I know
that Microsoft did it with Point, Rectangle, etc. (for specific
performance reasons), but that doesn't make it generally good practice,
because you end up with precisely the kinds of problems you're
outlining here.

[...]

Should it be easier to modify a struct field using something like a C++
method pointer? That sounds to me like the assertion that it really
should be easier to use a screwdriver as a hammer, and there's
something wrong with screwdrivers that doesn't permit them to be easily
used in that way.

I side with Pete Davis: What design problem caused you to choose a
struct over a class here?


The whole story is quite complex. It arises in the context of
unmarshaling data for the Ice middleware. If you want to know
all the details, check out the Slice chapter and the Protocol
chapter in the Ice documentation (http://www.zeroc.com/Ice-Manual.pdf).
Read up on structs and classes.

Basically, a Slice struct maps to a C# struct by default. Slice
structs can contain class members, which map to C# references.
And classes can form circular graphs. To unmarshal cycles of classes,
it is necessary to break the cycle somewhere. In turn, that means
that I must unmarshal a class instance before all the values of
its data members have been unmarshaled. The way the unmarshaling
code deals with this is to unmarshal the class instance and then
later, when an instance arrives that is referred to by a member of
the first instance, the code patches the corresponding member in
the first instance by assigning to it.

This isn't a problem for classes, because they are reference types,
but it is a problem for structures, which aren't.

The language mapping maps Slice structs to C# structs for efficiency
reasons--it's faster to instantiate a struct than to instantiate a
class.
Cheers,

Michi.
Nov 17 '05 #9

P: n/a
Abubakar wrote:
Hi,
if I compile your code under Rotor csc, it gives me the following error:
---------------------------------------
Unhandled Exception: System.NotSupportedException: Delegates on value
classes can only be formed on virtual methods
at System.Delegate.NeverCallThis(Object target, IntPtr slot)
at ConsoleApplication1.Class1.Main(String[] args)
---------------------------------------


Interesting! I don't have Rotor csc. Does the error go away
if you make assignToI() a virtual method?

Cheers,

Michi.
Nov 17 '05 #10

P: n/a
Inline

Willy.

"Michi Henning" <mi***@zeroc.com> wrote in message
news:%2*****************@TK2MSFTNGP12.phx.gbl...
Jon Skeet [C# MVP] wrote:


What do you expect it to do if the delegate "lives" for longer than the
current stack frame? What could it possibly operate on then?


When the delegate tries to call the struct member function, it could
throw an exception if the struct is no longer there.


Why? this call is perfectly valid, the method is not called on the value
type, it's called on a boxed copy, and the new value of the member i is set
on the boxed type (in your sample i which is 1 is set to 99), not on the
value type (on the callers stack). When you return from the delegate, the
boxed type is no longer referenced and will be collected when the GC comes
along.

Furthermore, how do you expect the delegate to maintain a reference to
the struct on the stack in the first place?


Well, that would be the job of the compiler.


The reference on the stack is maintained, no problem.
Note that this behaviour is well-defined in the spec - see section
14.5.10.3 of the ECMA spec.


Right. No argument there. The behavior is exactly as prescribed by
the spec. That doesn't make it any more useful though ;-)

I honestly can't think of why I would want to deliberately do this--
having a delegate call into a temporary anonymous copy of a value
type would never achieve anything useful, as far as I can see.
It would be nice if the compiler emitted a warning for this case.

Cheers,

Michi.

Nov 17 '05 #11

P: n/a
Thanks for the excellent explanation.

Nov 17 '05 #12

P: n/a
Hi, no the error doesnt go away. I had to make a lil change to the rotor
source to make it work. After discussing with few people I came to know that
this was a restriction only in v1 of the .net framework. In v1.1 and onwards
it was fixed. After my fix the output was ditto as it in C# 2.0 beta 2.

Ab.
http://joehacker.blogspot.com.
"Michi Henning" <mi***@zeroc.com> wrote in message
news:%2****************@TK2MSFTNGP12.phx.gbl...
Abubakar wrote:
Hi,
if I compile your code under Rotor csc, it gives me the following error:
---------------------------------------
Unhandled Exception: System.NotSupportedException: Delegates on value
classes can only be formed on virtual methods
at System.Delegate.NeverCallThis(Object target, IntPtr slot)
at ConsoleApplication1.Class1.Main(String[] args)
---------------------------------------


Interesting! I don't have Rotor csc. Does the error go away
if you make assignToI() a virtual method?

Cheers,

Michi.

Nov 17 '05 #13

P: n/a
btw, have u been able to workaround this issue yet?

Ab.

"Michi Henning" <mi***@zeroc.com> wrote in message
news:%2****************@TK2MSFTNGP12.phx.gbl...
Abubakar wrote:
Hi,
if I compile your code under Rotor csc, it gives me the following error:
---------------------------------------
Unhandled Exception: System.NotSupportedException: Delegates on value
classes can only be formed on virtual methods
at System.Delegate.NeverCallThis(Object target, IntPtr slot)
at ConsoleApplication1.Class1.Main(String[] args)
---------------------------------------


Interesting! I don't have Rotor csc. Does the error go away
if you make assignToI() a virtual method?

Cheers,

Michi.

Nov 17 '05 #14

P: n/a
Michi Henning <mi***@zeroc.com> wrote:
What do you expect it to do if the delegate "lives" for longer than the
current stack frame? What could it possibly operate on then?


When the delegate tries to call the struct member function, it could
throw an exception if the struct is no longer there.


That sounds like a fairly complicated way of doing things, and just as
unsatisfactory as the current behaviour IMO.
Furthermore, how do you expect the delegate to maintain a reference to
the struct on the stack in the first place?


Well, that would be the job of the compiler.
Note that this behaviour is well-defined in the spec - see section
14.5.10.3 of the ECMA spec.


Right. No argument there. The behavior is exactly as prescribed by
the spec. That doesn't make it any more useful though ;-)

I honestly can't think of why I would want to deliberately do this--
having a delegate call into a temporary anonymous copy of a value
type would never achieve anything useful, as far as I can see.
It would be nice if the compiler emitted a warning for this case.


It only wouldn't achieve anything useful if you wanted to change the
values, and as structs should almost always be immutable, it's very
rarely a problem.

--
Jon Skeet - <sk***@pobox.com>
http://www.pobox.com/~skeet Blog: http://www.msmvps.com/jon.skeet
If replying to the group, please do not mail me too
Nov 17 '05 #15

P: n/a
Hi,
I dont know if it will help or not but you can also access the changed
object throught

((SimpleStruct)assign_to_i.Target).i
after the call to CallDelegate.

Cheers.

Ab.

"Michi Henning" <mi***@zeroc.com> wrote in message
news:OU******************@TK2MSFTNGP09.phx.gbl...
Below is a bit of code that creates a delegate.
The delegate invokes a member function of a struct.
The code compiles, but has surprising behavior:

using System;

namespace ConsoleApplication1
{
public struct SimpleStruct
{
public int i;

public void assignToI(int x)
{
Console.WriteLine("Assigning i: " + x);
i = x;
Console.WriteLine("i = " + x);
}
}

class Class1
{
public delegate void Assigner(int s);

private static void callDelegate(Assigner a, int i)
{
a(i);
}

static void Main(string[] args)
{
SimpleStruct s = new SimpleStruct();
s.assignToI(1); // Works as expected, assigns 1 to s.i
System.Console.WriteLine("s.i after calling assignToI = " + s.i);
// Create a delegate that calls assignToI()
Assigner assign_to_i = new Assigner(s.assignToI);

// Call helper function, passing delegate and new value for s.i callDelegate(assign_to_i, 99);

System.Console.WriteLine("s.i after calling delegate = " + s.i); // Assignment is lost, s.i still contains 1!!!
}
}
}

When run, this code prints:

$ ./ConsoleApplication1.exe
Assigning i: 1
i = 1
s.i after calling assignToI = 1
Assigning i: 99
i = 99
s.i after calling delegate = 1

Note that the direct call s.assignToI() works as expected:
after the call, the structure member has new value.

However, the call via the delagate does not work as expected:
the value that is assigned is lost, and the structure member
has the old value.

Now, I realize what is going on here. The delegate constructor
expects an argument of type object. In other words, the expression

new Assigner(s.assignToI)

is internally converted to something like

new Delegate(s, "assignToI")

So, the struct is silently boxed and, when the delegate runs,
what it assigns to is a temporary copy of the struct on the heap,
instead of assigning to the real struct.

Several thoughts here:

1) I find this surprising. At no point in my code have I passed the
structure as a parameter, so I don't expect this behavior. (Yes,
I know that the struct ends up being boxed, but that is not manifest
anywhere in the code.)

2) I believe that the behavior is wrong. (Yes, I understand *why* it
works the way it does, but that doesn't necessarily make the behavior
right.) Delegates are the equivalent of C++ member function pointers.
If I create a delegate and pass a method of a struct, I expect the
delegate to call the method on the struct instance I specified, not
some temporary copy.

3) Even if we accept that the behavior is correct, then why does the compiler allow me to write this? After all, there is no way that such a delegate would ever do something useful, so why not at least emit a warning?

4) The silent boxing and unboxing in C# seems to be more of a curse than helpful. It is too easy to have a value type boxed, only to end up with invocations made to a boxed copy on the heap.

Finally, I'm looking for suggestions as to how I can achieve what the above code is trying to do. Basically, what I need to do is assign to a member of an already instantiated structure, but without knowing the type of the structure. That is, I want to, at least in spirit, be able to assign to a structure member via a pointer to the structure.
(This issue arises in the context of unmarshaling data from the wire and, for various legitimate reasons, I have to assign to a member of an already instatiated structure, instead of instantiating the structure after I have all its member values.)
I tried using pointers and unsafe code, but that only works for types that are unmanaged. However, the structure may contain managed members, in which case I can no
longer create a pointer to the struct, so this doesn't work either.

Any other ideas anyone?

Thanks,

Michi.

Nov 17 '05 #16

P: n/a
Abubakar wrote:
btw, have u been able to workaround this issue yet?


Yes, by changing the way the generated code works.
Basically, instead of trying to patch the struct directly,
the unmarshaling code patches a nested class instance.
Suppose the struct contains a reference member that needs patching:

// Slice
struct Foo {
int i;
SomeObject o;
double d;
};

The generated
struct looks something like:

public struct Foo {
public int i;
public SomeObject o;
public double d;

public sealed class Patcher__ : IceInternal.Patcher
{
internal Patcher__(Foo.PatchMembers__ instance)
{
_instance = instance;
}

public override void patch(Ice.Object v)
{
type_ = typeof(M.SomeObject);
_instance.o = (M.SomeObject)v;
}

private Foo.PatchMembers__ _instance;
}

internal class PatchMembers__
{
internal M.SomeObject o;
}

private PatchMembers__ pm_;

public void patch__()
{
o = pm_.o;
}

public void read__(IceInternal.BasicStream is__)
{
if(pm_ == null)
{
pm_ = new PatchMembers__();
}
i = is__.readInt();
is__.readObject(new Patcher__(pm_));
d = is__.readDouble();
}
}

The read__() method does the unmarshaling. It allocates a PatchMembers
inner class instance. The readObject() function is passed a Patcher__
instance. The patch() method of that instance is called by the generic
part of the unmarshaling code to patch the inner class instance.
So, once read__() completes, _instance.o has the right value, but the
o member of the struct itself doesn't.

The unmarshaling code for an operation that returns a Foo instance
then patches the struct like this:

M.Foo ret__;
ret__ = new M.Foo();
ret__.read__(is__);
is__.readPendingObjects(); // This unmarshals calls patch() on the
inner class
ret__.patch__(); // This copies the value in the inner class into the
struct member
return ret__;

This works because the patch__() method is invoked on the actual
instance, instead of a temporary boxed copy on the heap.

Not as elegant as for classes, which can be patched directly, but
it does the job.

Cheers,

Michi.
Nov 17 '05 #17

This discussion thread is closed

Replies have been disabled for this discussion.