Book review: Pro WPF in C# 2008

by Marc 27. February 2010 17:55
Book image Title: Pro WPF in C# 2008
Subtitle: Windows Presentation Foundation with .NET 3.5, Second Edition
Publisher: Apress® Books for Professionals by Professionals™
Series: The Expert's Voice in .NET
Author: Matthew MacDonald
Date of appearance: February, 2008 (1040 Pages)

This book is a thorough examination of the WPF 3.5 technology – its architecture, what you can do with it, and how to do it. It is directed at professional C# developers.

The book is written in a systematic, comprehensible way. A chapter starts with a basic introduction and includes graphics of the respective WPF sub-class hierarchy. The chapter's subject is then steadily explored in more and more detail. For instance, at the beginning, there are five pages just on resolution independence. XAML is explained in-depth, starting with the four ways of loading and compiling: Code-only, code and uncompiled XAML, code and compiled XAML, and XAML only; this is followed by the specifics of the XAML grammar (markup extensions, attached properties, etc.). There are seven pages on non-rectangular windows and sixteen pages on playing sound on different OS versions. As the book goes on, the author really shines in describing complex subjects, such as 3-D drawing, in a logically understandable way. Towards the end, there is a tabular overview of features missing in WPF compared to Windows Forms, with recommendations on when to choose one over the other, or both of them together, and how to mix them best.

The volume also contains lots of small, but precious pieces of surplus information, such as: Properties of WPF controls can be set in any order, without causing any change in behavior; or: By using an overloaded version of DependencyObject.SetValue in code, you can attach a value for any dependency property, even if it is not defined as an attached property (which is not possible in XAML). In addition, the author mentions various quirks of WPF, and how to get around them, if possible. Example: When you restart an animation that is almost complete, and the animation had the current position as the starting point, the animation will appear to slow down. Another example: Windows Vista always requires permission elevation for a setup, even though, in the case of Click Once, this makes no sense. As a consequence, a Click Once WPF application, on Vista, cannot be installed under a regular user account; the user is forced to install it under an admin account - which defeats the purpose of using Click Once in the first place...

Developers are all-too-familiar with the Pareto principle: 80% of the tasks of a project can be solved „easily“ in 20% of the time, but solving the other 20% takes at least 80% of the time. If you want to use WPF in a productive way, I strongly recommend taking the time to study this book. Admittedly, at 1040 pages, this is quite some endeavor. However, you will be rewarded many times over, as you will be saved a lot of frustration and unexpected delays, when you already know from the beginning how to solve much of the other 20%.

Yet another comment on the C# cast ( ) and „as“ operators…

by Marc 21. February 2010 19:39

Time and again, the question comes up whether to prefer the cast ( ) or the „as“ operator in C# to perform conversions to reference types. According to the C# 3.0 language specification, the two operators behave differently in several respects:

C# Conversion Operator( )as
Name Cast operator as operator
C# 3.0 Specification section 7.6.6 7.9.11
Can convert to ValueType Yes No
Executes user-defined explicit conversions Yes No
Compile-time errors CS0030: Cannot convert type 'X' to 'Y'. CS0030: Cannot convert type 'X' to 'Y'.
CS0077: The as operator must be used with a reference type or nullable type ('Y' is a non-nullable value type).
Runtime behavior if not convertible Throws InvalidCastException: "Unable to cast object of type 'X' to type 'Y'." Returns null

Given the above comparison table, it follows that you must use the cast operator ( ) in the following cases:

  • to convert to a ValueType:
    object o = 137; var i = (int)o;
  • to convert via a user-defined explicit operator overload defined in the source type. Note that the overload may return anything whatsoever: The same instance, a new instance of the same type, a new instance of a completely non-related type, null, a value type or reference type...
    // Declared inside a class called Source:
    static explicit operator Target(Source s) {return new Target();}

By contrast, you must use the "as" operator in the following cases:

  • to be 100% sure that that the result points to the same instance, or null, but never anything else:
    var bar = foo as Bar;
  • to return null if a runtime conversion to the desired reference type is not possible, as in this construct:
    var bar = foo as Bar ?? new Bar();

Furthermore, the "as" operator is a bit more tolerant with generics at compile time than the cast operator. That’s because the as operator casts „directly“ to a type of the same inheritance line:

TDerived GetDerived<TBase, TDerived>(TBase b) where TDerived: class {
    // Produces compiler error CS0030: Cannot convert
    // type 'TBase' to 'TDerived'.

    // return (TDerived)b;

    // This compiles...
    return
b as TDerived;
}

In all other cases, from a technical point of view, you may freely choose between the cast and the "as" operator. Therefore, the „right“ decision depends on what you want to express most in the source code. If you use the "as" operator, you thereby clearly document that you are interested in a reference target type that points to the same instance, and nothing else whatsoever – i.e., you do not want some crazy custom explicit operator to produce any side effects! Moreover, as a matter of taste, the "as" operator makes your code less cryptic, because it uses no parentheses, whereas the cast ( ) operator looks like old-fashioned C code.

For these reasons, personally, I prefer the "as" operator over the cast ( ) operator. However, I have met other developers who almost religiously defend usage of the cast ( ) operator as a best practice, „because it immediately throws an InvalidCastException“ at runtime if necessary, while the "as" operator might only later produce a NullReferenceException. Yet, against this, it could be argued that the "as" operator is even more secure, because it does not allow you to compile if it’s a value type, and it will never produce side effects...

Some tools, such as JetBrains‘ ReSharper, warn you if you apply the "as" operator without checking for null with a null comparison within the same procedure, even if null is logically impossible; e.g., when you have defined a null-checking procedure in a separate external helper method. You can turn this off, but only on a per-user basis. Others who look at your code may still see the warning. You may also disable the warning by surrounding the „critical“ section with a specific ReSharper comment, but it’s annoying to have your code cluttered like this. Therefore, if your company uses ReSharper, this might be a pragmatic reason to prefer the cast ( ) operator over the "as" operator.

In my opinion, it would be better if the C# "as" operator did not return null, but throw an InvalidCastException if the direct cast is not possible. Nothing of importance would be lost by this – you can always check for null with the "is" operator - but a lot of misunderstandings and disagreements would be prevented. Interestingly, the VB.NET language has the DirectCast operator, which does exactly this. A similar behavior could be implemented in C# like this:

public static T As<T>(this object o) where T: class{
    if
(o == null) throw new NullReferenceException();
    if
(!(o is T)) throw new InvalidCastException();
    return
o as T;
}

Simulating multiple inheritance in C#

by Marc 18. February 2010 23:15

It is possible to simulate multiple inheritance in C#, in a limited way, by applying extension methods to interfaces. An example is shown below. Note that this works only for method implementation inheritance, but not for properties or any other kinds of base members.

The C# team originally considered including extension properties in C# 4.0, but then dropped the feature. The reason apparently was that including extension properties would logically imply including indexed extension properties as well, which C#, by design, does not support. However, accessing (but not defining) indexed properties will be introduced in C# 4.0, in order to make it easier to work with COM interop. Another reason might be that extension properties in C# 4.0 would cause conceptual confusion with the already-existing feature of attached properties in WPF. Interestingly, F# does offer extension properties, but it cannot be used (yet?) as code-behind language for WPF XAML files.

You probably will not use this approach in order to „simulate multiple inheritance“ very often. However, the capacity to add extension methods to interfaces, as such, is a powerful feature. It allows to cleanly separate categories of standard behaviors from their owners.

 using System;

// This test shows how to simulate multiple inheritance in C#.

// Requires .Net Framework 3.5 as target framework.

namespace Test {

    interfaceIBase1 { }

 

    staticclassBase1Implementation {

        internalstaticvoid Base1Method(thisIBase1 base1) {

            Console.WriteLine("Base1Method called.");

        }

    }

 

    interfaceIBase2 { }

 

    staticclassBase2Implementation {

        internalstaticvoid Base2Method(thisIBase2 base2) {

            Console.WriteLine("Base2Method called.");

        }

    }

 

    classDerived: IBase1, IBase2 { }

 

    staticclassProgram {

        staticvoid Main() {

            var derived = newDerived();

            derived.Base1Method(); // Writes "Base1Method called."

            derived.Base2Method(); // Writes "Base2Method called."

        }

    }

}

Month List