473,379 Members | 1,220 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,379 software developers and data experts.

Beware CS1612 when dealing with "Point" (it's not really a structurelike most of us think of)

I ran afoul of this Compiler error CS1612 recently, when trying to
modify a Point, which I had made have a property. It's pointless to
do this (initially it will compile, but you'll run into problems
later). Apparently Point is a struct, a value type, and it does not
behave like a classic structure (in my mind's eye, and see below).
Traditionally I think of a classic structure as simply an object where
every member is public. But with "Point", this is not strictly true,
as the 'members' --namely Point.X and Point.Y--don't have meaning by
themselves. So it's Pointless (sic) to do this:

private _myPoint;
public Point myPoint
{
get { return _myPoint; }
set { _myPoint = value; }
}

You run a risk of CS1612 later (it's hard to explain, but this happens
sometimes when you don't use 'new' often enough, and/or don't use
temporary copies, see an example below at " // Use this instead", but
there are many permutations). It's better simply to make "myPoint"
public and be careful using it, or, if you insist on some protection
(which is debatable, see below), treat "myPoint" as a magic number
like an important integer, and use public 'get' / 'set' accessor
methods/functions (but apparently not properties, though you would
think properties are ideal for this task). Keep in mind myPoint is
like 'int' or 'nullable' types, and does not really have 'member
variables' like myPoint.X or myPoint.Y, even though it appears to in
the documentation (this is what tricked me).

Beware. A rule of thumb to avoid this is simply don’t treat Points
with properties, as you would use properties in an object. Or, as
better said by the poster "marko" below: "Apparently, C# does not
allow you to return a reference to a value type. In practical terms
this means that you cannot modify the fields
of a struct-based property."

I solved this in my program by making everything "point" simply
public, and that 'solved' the problem at a cost of some risk because I
wanted to check that Point was greater than zero (for my program) as
an object having a "property" (i.e., using pseudocode, for a property
myPoint, having a private member _myPoint: myPoint.Set-- if (value.X
0 && value.Y >0) , then _myPoint = value, otherwise not, to avoid
divide by zero;). But such is life. Now I'll have to check that the
point is zero (for each of its components, X,Y) everytime I use it
later, which is not a big deal but a bit of a hassle. See below on
this ‘point’ made by the poster: “you don't change ints: you calculate
new ones and set variables / properties to new values. Similarly, you
don't change Points: you calculate new ones and set variables /
properties to new values” (not sure this is technically accurate, but
I kind of see his point—no pun—you need to think of Points as
primitive data types like int, and the ‘property’ portion of a point,
namely, “Point.X” and “Point.Y” is a fiction, a shorthand way of
accessing the structure, like the ‘nullable’ structure, but Point is
not like a class or even a structure, but a primitive data type).
RL

The following code generates error CS1612.

Copy Code
// CS1612.cs
public struct MyStruct
{
public int Width;
}

public class ListView
{
public MyStruct Size
{
get { return new MyStruct(); }
}
}

public class MyClass
{
public MyClass()
{
ListView lvi;
lvi = new ListView();
lvi.Size.Width = 33; // CS1612
// Use this instead:
// MyStruct temp = lvi.Size;
// temp.Width = 33;
}

public static void Main() {}
}

///////////////////

http://www.codecomments.com/archive2...-5-503021.html
Author Modifying a struct-based property
Aleko
2005-05-19, 8:58 pm

Apparently, C# does not allow you to return a reference to a value
type. In practical terms this means that you cannot modify the fields
of a struct-based property. For example:

public class Thing
{
private Point _pos; // Point is a struct, a value type

public Point Position
{
get { return _pos; } // returns a _copy_ of _pos. Not what I had
in mind.
set { _pos = value; }
}
}

Later...

Thing t = new Thing;
t.Position.X = 100; // compiler error CS1612: cannot modify return
value

To add insult to injury, the program also incurs a slight performance
hit for allocating a temporary copy of the structure each time the
Position property is invoked. Am I right about this?

Maybe I'm missing something, but I just cannot see the purpose of such
a restriction. Where is the benefit of not allowing references to
value
types?

Thanks,
Aleko

Bruce Wood
2005-05-19, 8:58 pm

Because they're value types? :)

You're meant to think about value types as though they were ints or
doubles. You don't go into an int and fiddle with the third bit of
_that particular int_. In fact, the concept of "that particular int"
doesn't even make any sense. ints don't have identities: there is no
"this copy of the value 5" versus "that copy of the value 5". There's
just 5.

Of course, there are _variables_ that store ints, and you can modify
them. That is, in a nutshell, the point: you modify variables
containing values; you don't modify the values themselves.

And yes, whenever you return an int from a property, you get a copy,
not the original int. Allowing you to get a reference leads to C / C+
+,
meaning unsafe code:

myClassObject.IntProperty = 0;
int *x = &myClassObject.IntProperty;
*x = 5;

I don't think I need to go into the problems that this causes: that *x
= 5 bypasses any code in set_IntProperty designed to safeguard
encapsulation, etc. etc.

So, what does this have to do with Point? Everything! As I said, Point
is a value type, and you're meant to think of it in the same way as
you
think of an int or a double. You don't change ints: you calculate new
ones and set variables / properties to new values. Similarly, you
don't
change Points: you calculate new ones and set variables / properties
to
new values.

Yes, Points get copied onto the stack when returned from a property,
just as ints and doubles do. Think about what would happen if it were
not so:

myClassObject.PointProperty.X = 0;

bypasses all of the code in set_PointProperty designed to encapsulate
the state of myClass. What if set_PointProperty takes great pains to
ensure that the point never has an X of 0? Too bad: it was set through
the "back door".

The way you solve this problem with reference types (classes) is with
events: If you had a Point class, it would have to raise XChanged and
YChanged events so that myClass could intercept changes and throw
exceptions as appropriate. Ouch! Talk about overhead!

No, when dealing with value types, you have to set properties like
this:

myClass.PointProperty = new Point(100, myClass.PointProperty.Y);

Points are really small (64 bits), so pushing one on the stack is no
big deal. In fact, that's one of the recommendations for making
structs: keep them small, because they're copied all over the place,
just as ints and double are.

The only issue I have with the way that structs are implemented
in .NET
is that many common structs, like Point, have set accessors on some of
their properties. Personally, I think that this is utterly ridiculous.
It really doesn't buy you much to be able to say:

Point p = new Point(0, 0);
p.X = 50;

instead of:

Point p = new Point(0, 0);
p = new Point(50, p.Y);

and the former syntax only works if p is a variable, not, as you
pointed out, if it's a property. Neither does it work if the Point is
in an aggregate structure:

Hashtable h = new Hashtable();
h["Point"] = new Point(0, 0);
((Point)h["Point"]).X = 50;

does squat, because it's changing the X property of a copy of the
(boxed) Point in the hash table, not of the entry itself.

So, as I said, I think that set accessors on struct properties are
stupid. They just lead people to the false assumption that they can
treat structs as they treat classes, which isn't true. When I make my
own structs, I always make them invariant: if you want to change one
of
my value types, you either have to "new" a new one, or call some
method
on one that returns a new value. (A silly example for Point would be:

Point p = new Point(0, 0);
p = p.ChangeX(50);

this would always work, no matter what the situation. Sadly, they
didn't implement Point this way. I think they should have.)

Aleko
2005-05-25, 8:57 pm
>>Where is the benefit of not allowing references to value types?
>Because they're value types? :)
I know they are, but what's wrong with returning a reference to a
value
type? You can /pass in/ a value type by reference to a function (by
using the ref keyword), so why shouldn't you be able to pass it out?
It really doesn't buy you much to be able to say:
>Point p = new Point(0, 0);
p.X = 50;
>instead of:
>Point p = new Point(0, 0);
p = new Point(50, p.Y);
Hmm, I must disagree with this one. Assigning a value to a field is
many times cheaper than dynamically allocating a new copy of the
struct, copying _all the fields_, and finally having the garbage
collector clean up the copy. That's like buying a new walkman every
time the battery runs out, instead of just replacing the battery. :)
>So, as I said, I think that set accessors on struct properties are
stupid. They just lead people to the false assumption that they can
treat structs as they treat classes, which isn't true.
I agree, which is why I think struct fields should be public. Structs
are typically used when performance is an issue, so information hiding
in this case is not really a concern.

By extension struct-based class properties may also be publically
exposed without guilt, because they are, well, value types. You can't
do something dumb, like forget to allocate them, or assign null to
them. The worst you can do is assign some nonsensical value to a
field.
(If that's an issue, hide the fields behind accessors)

I suppose OO purists will stone me for suggesting violating the golden
rules, but the alternative is worse. I tried re-writing the Size
struct
as a class (with private fields, and accessors) so I could return it
by
value, and then change its properties. It worked fine, but I had to do
write an extra class, and I don't think I gained much by doing that.
Purity comes at a cost, and sometimes it's not worth it.

Regards,
Aleko

Bruce Wood
2005-05-25, 8:57 pm
Structs are typically used when performance is an issue
No! No! I blame Microsoft for this misconception. They have examples
of
doing this on MSDN and all. It's a terrible design decision. Yes, in a
few obscure cases you can gain something by using structs instead of
classes. However, the cases are, as I said, obscure... and rare.

As you have pointed out, using structs instead of classes causes lots
of copying, which can lead to performance _degradation_... unless you
pay very, very close attention to what you're doing. The source of all
of the confusion is the idea that structs are somehow "classes lite",
and that you should press them into service when you need screaming
performance. Of course, every newbie out there immediately starts
making everything a "struct" so that their app will "run faster", then
wonders why everything is so screwy. Aaargh. (Sorry... this is a pet
peeve of mine, doubly so because Microsoft seems to endorse this sort
of silliness. :)

Those few obscure and rare cases aside, structs should be employed in
only one situation: when you want _value semantics_. That is, when you
_want_ something that is copied rather than handled by reference. When
you want it to act like a _value_.

Here are couple of examples from my coding: a Fraction, and a Measure.
A Fraction is just what it looks like: it's a type capable of holding
a
whole part, a numerator, and a denominator. It acts like any other
number, and that's why it's a struct. I absolutely _don't_ want to
assign a Fraction to some variable, have that variable say
fracVar.Numerator = 3, and have the fraction change in that variable
_and_ the variable it was assigned from. That would be horrific. I
want
the thing to act like a _value_: like an int or a double or a decimal.
(This is, incidentally, precisely why my Fraction has no set
accessors,
so the snippet of code above isn't even legal in my world.) Similarly,
a Measure is a quantity that has a unit of measure (a reference type,
in my world) tagging along with it. Again, I want it to act like an
int
or a decimal. I _absolutely don't_ want reference semantics here. I
want value semantics.

IMHO there is a huge problem in the .NET community, wherein
programmers
try to press structs into service as a sort of "class lite" to
increase
performance, without really understanding what they're doing and thus
creating unnecessary headaches for themselves. structs are a valuable
part of the language. As I said, I've used them in a few places _where
they make sense_ and they're wonderful just the way they are. It's
when
you try to use them inappropriately that they start acting up. (The
only exception being what you ran into: putting Points and Sizes into
an aggregate structure is a perfectly reasonable thing to do. Then
wondering why you can't work with them as though they were reference
types is also perfectly reasonable. The problem is not that they don't
act like reference types. The problem is that somebody thought it was
clever to make Point and Size _mutable_, which they shouldn't be.)
finally having the garbage collector clean up the copy
This is inaccurate. The garbage collector does not have to clean up
the
copy: the copy must either live on the stack (no garbage collection
there), or within a reference type which would have had to have been
garbage collected anyway. The only time the garbage collector gets
involved with value types is if you box them, which effectively
creates
a reference type to hold the value. This is probably why Microsoft
chose to make Point and Size into value types: precisely to avoid
garbage collection of a bazillion little Points and Sizes when code
starts doing mathematics with them. _This_ is the sense in which
structs are "faster": they require no GC, and they require no heap
allocation. Constructing a new Point is, at run time, no more
expensive
than constructing a new double, and just like a double there is no
garbage collection required. It's a questionable design: I'm not sure
that the savings in GC and allocation time for Points and Sizes is
worth the confusion it's caused amongst programmers. The fact that
MSDN
contains an example of using a struct for a _customer record_ doesn't
help matters either (grrr...).
...which is why I think struct fields should be public.
.... which is useful only if you're using them for what I would claim
is
the "wrong purpose". If you use structs to implement new kinds of
_values_, then there's no need to go breaking encapsulation in an
attempt to make them act like reference types. Again, you could argue
that Points and Sizes as structs is a dodgy implementation, and I
would
agree. However, if you're making your own structs, and you make all of
the fields public and start trying to use them as though they were
reference types, I would claim that you're pushing on a rope. :)

As well, even doing this won't help your hash table situation: even if
all of Point's fields were public, you still couldn't do the
assignment
directly into the value that is boxed and stored in the hash table.
Value semantics would still bite you back. :)
Purity comes at a cost, and sometimes it's not worth it.
Absolutely true, and sometimes I decide to abandon clean code for
other
benefits. Just be sure, however, that you're not abandoning what you
call "purity" because you're using the wrong construct for what you
want to do. The only "bad" thing about structs is that in aggregates
(pre v2.0) you have to say:

Point oldPoint = (Point)myHash["Key"];
myHash["Key"] = new Point(oldPoint.X, newY);

instead of

((Point)myHash["Key"]).Y = newY;

It has nothing to do with performance... it's all about how much
typing
is required. I agree. the source code is ugly, and v2.0 won't be much
better:

myHash["Key"] = new Point(myHash["Key"].X, newY);

But IMHO it's not worth abandoning or warping the feature because in
one situation it doesn't "play nice", when it's so useful in other
contexts.

Aug 21 '08 #1
0 2168

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

2
by: Randy Crockett | last post by:
I have created a very simple DLL in VC++ 6.0, then created a very simple App in VC++ 6.0 to use the DLL and this works fine When I try to use the same DLL in C#, with DLLImport, the app finds the...
0
by: Lionel B | last post by:
Greetings, Using gcc (GCC) 3.3.3 (cygwin special) on Win2K Does anyone know if there is a convenient mechanism to cause a C++ exception to be thrown whenever a IEEE 754 floating-point...
6
by: Martin Heuckeroth | last post by:
Hi, We are looking for a way to determine the x and y points of the cursor in a richtext box. We made an VB.NET application with a couple of listboxes and one of them is a richtextlistbox....
5
by: hzgt9b | last post by:
I'm building a dataset that writes out a Point type value. Here's the code that I've got: 1 Dim dsContent As DataSet = New DataSet("content") 2 Dim dtAsset As DataTable = New...
45
by: Gregory Petrosyan | last post by:
1) From 2.4.2 documentation: There are two new valid (semantic) forms for the raise statement: raise Class, instance raise instance 2) In python: >>> raise NameError Traceback (most recent...
14
by: Alex | last post by:
I saw the topic of "wired code " about "point to array" and know a little about it. But I am still confused about the question below: I define a point to array "b" int (*b); then I locate the...
169
by: JohnQ | last post by:
(The "C++ Grammer" thread in comp.lang.c++.moderated prompted this post). It would be more than a little bit nice if C++ was much "cleaner" (less complex) so that it wasn't a major world wide...
6
by: =?utf-8?B?4piG4piG4piG4piG4piGIFPDvCBLZWl0aCBDaGFr | last post by:
Ok. Let's say Im writing a game and I have a class table for my character's stats. (Don't worry about classes yet ,you'll run into them soon enough). Say this table includes all his stats, age,...
0
by: Faith0G | last post by:
I am starting a new it consulting business and it's been a while since I setup a new website. Is wordpress still the best web based software for hosting a 5 page website? The webpages will be...
0
isladogs
by: isladogs | last post by:
The next Access Europe User Group meeting will be on Wednesday 3 Apr 2024 starting at 18:00 UK time (6PM UTC+1) and finishing by 19:30 (7.30PM). In this session, we are pleased to welcome former...
0
by: ryjfgjl | last post by:
In our work, we often need to import Excel data into databases (such as MySQL, SQL Server, Oracle) for data analysis and processing. Usually, we use database tools like Navicat or the Excel import...
0
by: Charles Arthur | last post by:
How do i turn on java script on a villaon, callus and itel keypad mobile phone
0
by: ryjfgjl | last post by:
If we have dozens or hundreds of excel to import into the database, if we use the excel import function provided by database editors such as navicat, it will be extremely tedious and time-consuming...
0
by: ryjfgjl | last post by:
In our work, we often receive Excel tables with data in the same format. If we want to analyze these data, it can be difficult to analyze them because the data is spread across multiple Excel files...
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?
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...

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.