473,414 Members | 1,563 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,414 software developers and data experts.

Structs and delegates

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
16 1842
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
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
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
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
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
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
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
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
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
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
Thanks for the excellent explanation.

Nov 17 '05 #12
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
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
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
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
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 thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

23
by: Brian | last post by:
I am very new to C# programming and have run into a problem. I apologize for the repost of this article. For some reason, my news reader attached it to an existing thread. First off, I have...
4
by: LP | last post by:
Hello! I am still transitioning from VB.NET to C#. I undertand the basic concepts of Delegates, more so of Events and somewhat understand AsyncCallback methods. But I need some clarification on...
8
by: Mas L via DotNetMonster.com | last post by:
Hi, I have a c++ source code which I can compile to be a DLL (in VS.NET 2003). And I need to use it in a C# program. After I compiled/build the C++ code to a DLL, I add it as a Reference in my...
14
by: Jeff S. | last post by:
In a Windows Forms application I plan to have a collection of structs - each of which contains a bunch of properties describing a person (e.g., LastName, FirstName, EmployeeID, HomeAddress,...
6
by: =?Utf-8?B?Sko=?= | last post by:
I have a logger component that logs to multiple sources, ie textfile, eventlog etc. and I have two methods that depending on where I call up my logger comp. one of them will be called. For ex. if...
0
by: bharathreddy | last post by:
Delegates Here in this article I will explain about delegates in brief. Some important points about delegates. This article is meant to only those who already know delegates, it will be a quick...
6
by: =?Utf-8?B?T2xkQ2FEb2c=?= | last post by:
My question is regarding the use of delegates in C#. I see how .Net uses delegates to wire event handlers to events. It’s an object created by a single line of code by the system and that makes...
7
by: Siegfried Heintze | last post by:
I'm studying the book "Microsoft Visual Basic.NET Language Reference" and I would like some clarify the difference between events and delegates. On page 156 I see a WinForms example of timer that...
69
by: raylopez99 | last post by:
They usually don't teach you in most textbooks I've seen that delegates can be used to call class methods from classes that are 'unaware' of the delegate, so long as the class has the same...
0
by: emmanuelkatto | last post by:
Hi All, I am Emmanuel katto from Uganda. I want to ask what challenges you've faced while migrating a website to cloud. Please let me know. Thanks! Emmanuel
1
by: nemocccc | last post by:
hello, everyone, I want to develop a software for my android phone for daily needs, any suggestions?
1
by: Sonnysonu | last post by:
This is the data of csv file 1 2 3 1 2 3 1 2 3 1 2 3 2 3 2 3 3 the lengths should be different i have to store the data by column-wise with in the specific length. suppose the i have to...
0
by: Hystou | last post by:
There are some requirements for setting up RAID: 1. The motherboard and BIOS support RAID configuration. 2. The motherboard has 2 or more available SATA protocol SSD/HDD slots (including MSATA, M.2...
0
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers,...
0
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven...
0
tracyyun
by: tracyyun | last post by:
Dear forum friends, With the development of smart home technology, a variety of wireless communication protocols have appeared on the market, such as Zigbee, Z-Wave, Wi-Fi, Bluetooth, etc. Each...
0
agi2029
by: agi2029 | last post by:
Let's talk about the concept of autonomous AI software engineers and no-code agents. These AIs are designed to manage the entire lifecycle of a software development project—planning, coding, testing,...
0
by: conductexam | last post by:
I have .net C# application in which I am extracting data from word file and save it in database particularly. To store word all data as it is I am converting the whole word file firstly in HTML and...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.