Thursday, December 18, 2008
Comparing Default Values In Generic Classes
Ever since they were introduced in C# 2.0, most programmers have loved generics. I know I do! But, it can be a bit tricky to determine if a variable has been assigned or is still the default value if you do not know how to do it.
Consider the following class, and note that it
The code
So, the question becomes just how do you do such a comparison if you cannot do that? If you know
But, that will only work for reference types and we have not put any such constraints on
Actually, as a side note, that code will compile for both reference types and value types but will always return false for value types. This is because, believe it or not, starting with C# 2.0, you can compare any value type to null using the
So, if you cannot compare against
With that change to the
Consider the following class, and note that it
- Is a generic class
- Has a field name
_field
- Initialize
_fieldto the default value in the constructor
- Has a property
IsDefaultValuethat should return true if_fieldis still the default value (or has been assigned back the default value).
public class MyClass
{
private T _field;
public MyClass()
{
_field = default(T);
}
public T Field
{
get
{
return _field;
}
set
{
_field = value;
}
}
public bool IsDefaultValue
{
get
{
// The following line does not compile!
return ( _field == default(T) );
}
}
}
The code
default(T) causes the compiler to use the default that is appropriate for the type (null for refrence types, 0 for an int, 0.0 for a double, etc.). This is used in the constructor to assign the default value to _field. Therefore, it seems logical that we can do a comparison such as ( _field == default(T) ). But, the compiler does not like it and will complain with a message like Operator '==' cannot be applied to operands of type 'T' and 'T'.So, the question becomes just how do you do such a comparison if you cannot do that? If you know
T is a reference type, you can compare against null.
// Works only if T is a reference time
return ( _field == null );
But, that will only work for reference types and we have not put any such constraints on
T.Actually, as a side note, that code will compile for both reference types and value types but will always return false for value types. This is because, believe it or not, starting with C# 2.0, you can compare any value type to null using the
== operator, but the comparison will always return false. This is a side effect of adding support for nullable types via ?.
int x = 1;
if (x == null)... // Always returns false
int? y = null;
if (y == null)... // Returns true since y is null
y = 1;
if (y == null)... // Return false since y is not null
if (x == y) ... // Return true since x and y have the same value of 1
So, if you cannot compare against
default(T) or null, what can you do to see if _field is currently set to the default value? The best way I have found to do this is shown below.
public bool IsDefaultValue
{
get
{
return object.Equals(_field, default(T) );
}
}
With that change to the
IsDefaultValue method, it compiles and works as you would expect.
MyClass<int> obj1 = new MyClass<int>();
bool flag1a = obj1.IsDefaultValue; // true
obj1.Field = 1;
bool flag1b = obj1.IsDefaultValue; // false
MyClass<object> obj2 = new MyClass<object>();
bool flag2a = obj2.IsDefaultValue; // true
obj2.Field = new object();
bool flag2b = obj1.IsDefaultValue; // false
Friday, December 5, 2008
WPF and TextDecoration
In WPF, many classes have a
When using XAML, things just kind of automagically work. The following example reads very nice, and leaves not doubt that the text will be both underlined and overlined.
But, when using text decorations from code, things are a bit on the weird side.
For starters, consider the following properties of the
Sounds like a list of predefined text decorations, so what's weird with that? I find it very weird that each of those properties is actually of type
Presumably, Microsoft chose to make those properties collections so that you could directly set them in code to a
And, they overload the
Now for the part I find very weird. Let's say I created a TextDecorationCollection instance and added to it
Or, I could also create a new instance using the overload that acts like a copy constructor.
TextDecorations property of type TextDecorationCollection. That property is used to add any combination of overline, strike through, underline, baseline or custom decorations to text. (For the rest of this post, I will be ignoring custom and sticking with the standard four).When using XAML, things just kind of automagically work. The following example reads very nice, and leaves not doubt that the text will be both underlined and overlined.
<TextBlock TextDecorations="Underline, Overline">
The quick red fox
</TextBlock>
But, when using text decorations from code, things are a bit on the weird side.
For starters, consider the following properties of the
TextDecorations static class: TextDecorations.Baseline, TextDecorations.Overline, TextDecorations.Strikethrough and TextDecorations.Underline.Sounds like a list of predefined text decorations, so what's weird with that? I find it very weird that each of those properties is actually of type
TextDecprationCollection which implies it could have multiple items within the collection.Presumably, Microsoft chose to make those properties collections so that you could directly set them in code to a
TextDecorations property.
TextBlock textBlock = new TextBlock();
textBlock.TextDecorations = TextDecorations.Underlined;
And, they overload the
TextDecorationCollection.Add() method so that it can take an IEnumerable(TextDecoration) (i.e. another TextDecorationCollection, etc.).Now for the part I find very weird. Let's say I created a TextDecorationCollection instance and added to it
TextDecorations.Underline, and then set a TextDecoration property to that collection. Then, if I add or remove TextDecorations to or from the TextDecorationCollection collection after it has already been set to the property, those changes are not rendered. One way around this is to set the property to null, add the decorations to the collection, and then set it back.
TextDecorationCollection tdc =
textBlock.TextDecorations;
textBlock.TextDecorations = null;
tds.Add(TextDecorations.Strikethrough);
textBlock.TextDecorations = tds;
Or, I could also create a new instance using the overload that acts like a copy constructor.
TextDecorationCollection tdc =
new TextDecorationCollection(textBlock.TextDecorations);
tds.Add(TextDecorations.Strikethrough);
textBlock.TextDecorations = tds;
?, ?? and Casting
I have previous written a post on the use of the by declaring it with the
Also, you may recall that when you use the value of a nullable type, you must also supply a value to use if the value of the nullable type is null. If you think about it, this makes perfect sense. If
But what if you already know the value is not null? Do you still have to supply a "dummy" value to use for null even though you know it will never be used? The answer is no, because you could always cast it to a non-nullable type:
The casting approach is not so handy in the example above because it's just a more verbose way of doing the same calculation, but it does illustrate the point. And, there are many times, where the casting is the way to go. For example, what if you wanted to call a different method based on whether the value was
? and ?? operators and nullable types. If you recall, you can make any value type ? operator:
int? myIntA;
Also, you may recall that when you use the value of a nullable type, you must also supply a value to use if the value of the nullable type is null. If you think about it, this makes perfect sense. If
myIntA was null in the example above, what the heck does it mean to 100 to myIntA? The compiler has no idea, so you must tell it, which is really one of the main reasons why the ?? operator was added.
int myIntB = 100 + (myIntA ?? 0);
But what if you already know the value is not null? Do you still have to supply a "dummy" value to use for null even though you know it will never be used? The answer is no, because you could always cast it to a non-nullable type:
if (myIntA == null)
{
myIntB = 100;
}
else
{
myIntB = 100 + (int)myIntA;
}
The casting approach is not so handy in the example above because it's just a more verbose way of doing the same calculation, but it does illustrate the point. And, there are many times, where the casting is the way to go. For example, what if you wanted to call a different method based on whether the value was
null or not?
if (myIntA == null)
{
Method1();
}
else
{
Method2( (int)myIntA );
}
Subscribe to Posts [Atom]