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



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 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 ? and ?? operators and nullable types. If you recall, you can make any value type by declaring it with the ? 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 );
}

 

This page is powered by Blogger. Isn't yours?

Subscribe to Posts [Atom]