Using property to reference Struct data

  • Thread starter Thread starter Guest
  • Start date Start date
G

Guest

I don't understand why I cannot use a property to modify data within a
struct. Can someone tell me why I get the error "Cannot modify the return
value of "myData.TheData" because it is not a variable.

Here is what breaks.

struct Data
{
public Data(){}
private int someData;
public int TheData
{
get { return someData; }
set { someData = value; }
}
}

class Sample
{
Data myData = new Data();

void AMethod()
{
myData.TheData = 7;
}
}

If I don't use the property "TheData" and instead make "int someData" public
I can reference myData.someData = 7 and it will work just fine. Why can't a
property be used?
 
The property construct is fine.

I'm wondering why you declared a default constructor for a struct,
though, since C# doesn't allow them. Try removing the constructor and
see if it works.
 
This isn't the code that gave you that warning. This code fails to
compile with the error, "Structs cannot have parameterless
constructor." Please post the exact code that gave you the error.
 
Adding the constructor was my mistake. I don't have it in the actual code.

A MS help comment says:

Error Message
Cannot modify the return value of 'expression' because it is not a variable

An attempt was made to modify a value type that was the result of an
intermediate expression. Because the value is not persisted, the value will
be unchanged.

To resolve this error, store the result of the expression in an intermediate
value, or use a reference type for the intermediate expression.


I therefore created a temp value and assigned my structure to it. The only
thing is that when updating the temp reference it didn't update my structure.

Data tmp = myData;
tmp.TheData = 7;

This assigned 7 to TheData in the tmp value, but it didn't update the myData
"data". I can't figure out way.

myData is part of a static global structure. Could the static nature have
anything to do with it?
 
I copied your code in to a VS2003 project, removed the spurious
constructor, and it compiles with no warnings or errors.

Please post the exact code that caused this compiler error. I suspect I
know what's going on, but I can't say until I see the real code.
 
Here is my code, I trimmed out the stuff that wasn't necessary.

Create a project called TestSamples with a form that does nothing for now.
Simple insert this code and it will compile with the error I indicated. If
you change "public struct TimeDelayEventData" to "public class
TimeDelayEventData" then it works just fine.

This code is my "GlobalData class"

using System;
using System.Collections;

namespace TestSamples
{
/// <summary>
/// Summary description for Class1.
/// </summary>
internal class GlobalData
{
// Explicit static constructor to tell C# compiler
// not to mark type as beforefieldinit.
static GlobalData() {}
/// <summary>
/// Default constructor
/// </summary>
private GlobalData()
{
// Set up the global data structures
this.timeDelayData = new TimeDelayEventData();

// Set up event handlers for the time delay events
this.timeDelayRequestor = new TimeDelayRequestEvents();
this.timeDelayListener = new TimeDelayFullfillEvents();
this.timeDelayListener.AddEvent(this.timeDelayRequestor);
}

/// <summary>
/// Create a static instance of this class.
/// </summary>
static readonly GlobalData instance = new GlobalData();
/// <summary>
/// References the data regarding the time delays for the testing
/// </summary>
private TimeDelayEventData timeDelayData;
/// <summary>
/// Reference to the event handler to fulfill the event of passing
time delay data to the global data structure
/// </summary>
private TimeDelayFullfillEvents timeDelayListener;
/// <summary>
/// Reference to the event handler to request time delay events
/// </summary>
private TimeDelayRequestEvents timeDelayRequestor;

/// <summary>
/// Get a reference to this instance of this class.
/// </summary>
public static GlobalData Instance
{
get { return GlobalData.instance; }
}

/// <summary>
/// Get a reference to the time delay data.
/// </summary>
public TimeDelayEventData MyTimeDelayData
{
get
{
return this.timeDelayData;
}
}

/// <summary>
/// Get a reference to the time delay requestor
/// </summary>
public TimeDelayRequestEvents MyTimeDelayRequestor
{
get
{
return this.timeDelayRequestor;
}
}
}
}


Here is my TimeDelayEventData struct...

using System;
using System.Collections.Generic;
using System.Text;

namespace TestSamples
{
public struct TimeDelayEventData
{
/// <summary>
/// Ignore the date when asked to run the test
/// </summary>
private bool ignoreDate;
/// <summary>
/// This enum identifies the data that is to be updated
/// </summary>
private TimeDelayIdentifiers timeDelayIdentifier;

/// <summary>
/// Get or set the flag indicating that the date should be ignored
when asked to run the test
/// </summary>
public bool IgnoreDate
{
get
{
return ignoreDate;
}
set
{
ignoreDate = value;
}
}


/// <summary>
/// Get or set the identifier that identifies the data to be updated
in the event
/// </summary>
public TimeDelayIdentifiers Identifier
{
get
{
return timeDelayIdentifier;
}
set
{
timeDelayIdentifier = value;
}
}
}
}

Here is my event handler classes...

using System;
using System.Collections.Generic;
using System.Text;

namespace TestSamples
{
/// <param name="hourData">This structure identifies and contains the
data to be sent to the event</param>
public delegate void TimeDelayEventHandler(TimeDelayEventData data);

public class TimeDelayRequestEvents
{
/// <summary>
/// This event requests an update to the hour data.
/// </summary>
public event TestSamples.TimeDelayEventHandler
timeDelayUpdateRequestEvent;

/// <summary>
/// Default constructor
/// </summary>
public TimeDelayRequestEvents()
{
}

/// <summary>
/// Issue the request to update the hour data.
/// </summary>
/// <param name="obj">Time Delay data to be updated when an event
occurs</param>
private void RequestUpdateEvent(TimeDelayEventData obj)
{
if (timeDelayUpdateRequestEvent != null)
timeDelayUpdateRequestEvent(obj);
}

/// <summary>
/// Make the request to update the data being passed for time delay
information.
/// </summary>
/// <param name="obj">Time Delay data to be udpated when an event
occurs</param>
public void OnUpdate(TimeDelayEventData obj)
{
RequestUpdateEvent(obj);
}
}

/// <summary>
/// This class handles all fullfillments requests for updating the
global data representing the time delay data.
/// </summary>
public class TimeDelayFullfillEvents
{
/// <summary>
/// References the request to handle a time delay event
/// </summary>
private TimeDelayRequestEvents requestEvent = null;
/// <summary>
/// Default constructor
/// </summary>
public TimeDelayFullfillEvents()
{
}

/// <summary>
/// Remove any previous event instance.
/// </summary>
protected void RemoveEvent()
{
if (this.requestEvent != null)
this.requestEvent.timeDelayUpdateRequestEvent -= new
TimeDelayEventHandler(this.UpdateTimeDelayData);
}

/// <summary>
/// Update the data for the time delay information
/// </summary>
private void UpdateTimeDelayData(TimeDelayEventData data)
{
switch (data.Identifier)
{
case TimeDelayIdentifiers.IgnoreDate:
GlobalData.Instance.MyTimeDelayData.IgnoreDate =
data.IgnoreDate;
break;
}
}

/// <summary>
/// Add the event handler for the time delay events
/// </summary>
/// <param name="request">Requesting event to update the time delay
data</param>
public void AddEvent(TimeDelayRequestEvents request)
{
// Remove any instances of an old request.
this.RemoveEvent();

// Create the new handler.
this.requestEvent = request;
this.requestEvent.timeDelayUpdateRequestEvent +=new
TimeDelayEventHandler(this.UpdateTimeDelayData);
}
}
}


Here is my TimeDelayIdentifiers...

using System;
using System.Collections.Generic;
using System.Text;

namespace TestSamples
{
/// <summary>
/// This enum is used to identify which data in the event is to be updated
/// </summary>
public enum TimeDelayIdentifiers
{
/// <summary>
/// Indicates the data is in the ignoreDate field
/// </summary>
IgnoreDate = 1,
}
}
 
I presume that you have a class that has, as one of its members, a
variable of a structure type and that you are trying to do something
like this:

ClassVariable.Structure.StructureProperty = 1;

You are getting the error because when you call ClassVariable.Structure
you are getting a *copy* of the structure and not a reference to the
actual structure. So any changes you make to *copy* will not be copied
to the structure in the class.

You need to do something like this:

//Get the copy of the structure and save in a variable
MyStruct tmp = ClassVariable.Structure;

//Modify the copy
tmp.StructureProperty = 1;

//Replace the structure in the class instance with the copy.
ClassVariable.Structure = tmp;

Or, instead of a structure, use a class then you wont have to create a
tmp copy since you will be working with references.
 
And the line that causes the compiler error is:

GlobalData.Instance.MyTimeDelayData.IgnoreDate = data.IgnoreDate;

in class TimeDelayFullfillEvents. Right?

Remember that structs have _value semantics_. That means that when you
say: GlobalData.Instance.MyTimeDelayData, you are getting a _copy_ of
the TimeDelayData within the GlobalData object, not a _reference_ to
the TimeDelayData.

So, when you say MyTimeDelayData.IgnoreDate = ... what you're saying
is, "Return me a copy of the TimeDelayData from the GlobalData object,
then make a change to that copy, then throw it away." Throw it away
because you don't store the copy anywhere, so the compiler chucks it.

Mutable structs are strange beasts and tricky to work with. Unless
you're going to be creating and destroying millions (and I do mean
_millions_) of these things, you're better off making it a class, not a
struct. Then it will act in a more reasonable manner. The other
possibility is to make it immutable (no sets in any of your struct's
properties), which forces you to create a "new" one and assign it to
the property, rather than trying to set _its_ properties.
 
With your explanation and Bruce's this has clarified for me my mistake.
Thanks to you both.
 
This explanation has clarified for me my mistake. Thank you for you time.
I'll use it as a class.

Would you mind looking at my post "An Event Listener" posted today? Maybe
you can help me there as well.
 
Hi Steve,

Thanks for your post.

I modified your code snippet into the following runnable code, however, it
does not get any runtime error on my side:

struct Data
{
private int someData;
public int TheData
{
get { return someData; }
set { someData = value; }
}
}

class Sample
{
public static void AMethod()
{
Data myData = new Data();
myData.TheData = 7;
}
}

static void Main()
{
Sample.AMethod();
}

Can you show me some more information to help us reproduce this problem?

I look forward to hearing from you. Thanks

Best regards,
Jeffrey Tan
Microsoft Online Partner Support
Get Secure! - www.microsoft.com/security
This posting is provided "as is" with no warranties and confers no rights.
 
Steve Teeples said:
Adding the constructor was my mistake. I don't have it in the actual code.

It's really, really important that you post *actual* code which you've
tried to compile. If you take out the extra constructor, the code
you've posted works fine.
A MS help comment says:

Error Message
Cannot modify the return value of 'expression' because it is not a variable

An attempt was made to modify a value type that was the result of an
intermediate expression. Because the value is not persisted, the value will
be unchanged.

Right - and that *doesn't* happen in the test code you showed.
To resolve this error, store the result of the expression in an intermediate
value, or use a reference type for the intermediate expression.

I therefore created a temp value and assigned my structure to it. The only
thing is that when updating the temp reference it didn't update my structure.

No, it wouldn't - because that's not how value types work. When you've
fetched the value, that's created a copy of it. Modifying that value
doesn't modify the value which was originally copied.
 
Steve Teeples said:
Here is my code, I trimmed out the stuff that wasn't necessary.

With respect, you didn't really. This is just for future reference,
really. It's worth being able to come up with a "short but complete"
program that demonstrates a problem. You can do this either by keeping
on removing code from something that fails, or you can add code to
something that works.

In this case, you could have removed all the event stuff without
affecting the problem you were having. Here's an example:

using System;

class GlobalData
{
private GlobalData()
{
this.timeDelayData = new TimeDelayEventData();
}

static readonly GlobalData instance = new GlobalData();

private TimeDelayEventData timeDelayData;

public static GlobalData Instance
{
get { return GlobalData.instance; }
}

public TimeDelayEventData MyTimeDelayData
{
get
{
return this.timeDelayData;
}
}
}

public struct TimeDelayEventData
{
private bool ignoreDate;

public bool IgnoreDate
{
get
{
return ignoreDate;
}
set
{
ignoreDate = value;
}
}
}

class Test
{
static void Main()
{
GlobalData.Instance.MyTimeDelayData.IgnoreDate = false;
}
}

Now, you can go further than this, because you don't really need the
singleton to be a singleton:

public struct TimeDelayEventData
{
private bool ignoreDate;

public bool IgnoreDate
{
get
{
return ignoreDate;
}
set
{
ignoreDate = value;
}
}
}

class Test
{
private TimeDelayEventData timeDelayData =new TimeDelayEventData();

public TimeDelayEventData MyTimeDelayData
{
get
{
return this.timeDelayData;
}
}

static void Main()
{
Test t = new Test();
t.MyTimeDelayData.IgnoreDate = false;
}
}


Now *that's* an example without any unnecessary stuff. Instead of about
250 lines of code, it's 36!
 
Bruce Wood said:
I cannot believe that you took the time to do that. <grin>

I figure that if the message hits home, it could easily save me that
much time - maybe more - the next time, when I might not have the spare
time to do it :)
 
Back
Top