views:

111

answers:

5

An example would be:

XNamespace ns = "my namespace"

Why not?:

XNamespace ns = new XNamespace ( "my namespace" )

What's the idea behind using implicit/explicit convertions instead of constructors? Convenience?

Is there a guideline for this?

+6  A: 

Convenience?

More or less, yes. Consider the case for when you’ve got a number-like object (say, a Complex) on which you do calculations. Clearly, writing code such as:

Complex result = c1 * new Complex(2) + new Complex(32);

is very annoying and hard to read. Implicit conversions help here (an alternative would be operator overloads in this example, but that would lead to lots of similar overloads).

Is there a guideline for this?

Provide as few implicit conversions as possible, since they may hide problems. Implicit conversion reduce explicitness by the same amount by which they increase terseness. Sometimes this is good, but sometimes not.

I find it best to restrict implicit conversions to very similar types, such as the number-like objects in my example above: an int essentially is-a Complex (from a mathematical standpoint; even if it’s not modelled via inheritance), hence an implicit conversion makes sense.

In VB, an implicit conversion is called “Widening” (as opposed to Narrowing, which is explicit) and this describes it well: no information is lost in the course of the conversion.

Furthermore, an operator is essentially a builder function, and has (some of) the usual advantages of a builder function over a constructor: namely, it can re-use cached values instead of always creating new instances.

Consider my Complex example. We may want to cache values for often-used Complex numbers:

Class Complex {
    // Rest of implementation.

    private static Complex[] cache = new[] {
        new Complex(-1), new Complex(0), new Complex(1) };

    public implicit operator Complex(int value) {
        if (value >= -1 && value <= 1)
            return cache[value];
        else
            return new Complex(value);
    }
}

Of course, whether this micro-optimization is effective is another question.

Konrad Rudolph
Thanks, how would you do it for operator overloads that would help the Complex type? You need to be able to edit the int type, right?
Joan Venge
@Joan: No, since you can implement operators in both directions: *from* your custom type and *to* your custom type. In my example, simply have `Complex` implement an operator `implicit operator Complex(int value)`.
Konrad Rudolph
Thanks I get it now.
Joan Venge
@Konrad: You can create implicit conversions both ways, but it's unwise to implement them both ways, as circular implicit conversions can cause ambiguities. Short example; abstract A has children B and C that can each implicitly cast to the other. myA = myB ?? myC; will confuse the compiler; are you setting myA to an instance of a B or a C?
KeithS
@Konrad, In addition, in the specific case of a Complex, there is not a one-to-one correspondence between int (or even double) and Complex values. The only time I use two-way implicit conversions is when the new type is really just a 'named' instance of a primitive type, such as a 'Force' type which is just a double with the added conceptual constraint that it means 'a force in Newtons'. Even then, it's sometimes helpful to make one conversion explicit, to demonstrate how a particular value is being interpreted.
Dan Bryant
@KeithS, @Dan: you are absolutely right. I wasn’t suggesting that you should implement them for both ways. Take the Complex example: obviously, an implicit conversion int -> Complex makes sense because there’s an exact mapping, while the opposite doesn’t make sense since there’s a loss of information.
Konrad Rudolph
+3  A: 

The use of implicit / explicit conversions is issue of convenience and one that many programming guidelines suggest you avoid in favor of explicit ConvertToXXX methods.

One of the problems is tha the use of implicit / explicit conversions further overloads functions of the casting operator. It gives it the dual purpose of

  • Viewing the same object through a different type / interface in the object's hierarchy
  • Coverting the object to a new type altogether

Unfortunately C# already does the latter in other areas (with primitives and boxing).

JaredPar
+1  A: 

Personally, I use the conversions when I know that the rhs may be converted into a static member of a class (like saying color = "red" to imply color = Colors.Red)

I use the new operator when I intend to actually create a new instance.

JamesMLV
+3  A: 

If two classes should be convertible to one another, but they do not share an interface of a base class that allows this behavior automatically, you would use the conversions. Implicit conversions should never have a possibility of data loss; they are often considered "widening" conversions. For example, converting an int to a long is a widening conversion, and there is no problem inherent in the conversion being implicit. Explicit conversions may involve the possibility of data loss; a long may or may not be convertible to an int, depending on its value.

One trick I have used with implicit conversions is to convert classes in different namespaces to each other when I did not have another reasonable option. For example, one WCF service returns an AuthenticationToken object that I need to pass to a WCF service in a different namespace. Both have this AuthenticationToken object, and constant conversion would have been a pain. My solution involved using public static implicit operator in a partial class to add the functionality to convert each way.

Andrew
+4  A: 

One of the reasons behind using implicit conversion with such simple types as XName is, I believe, convenience in calling methods.

For example, you can write

var info = root.Elements ("user").Element ("info").Value;

Simplicity at extracting data is what LINQ is all about, and if we had to write

var info = root.Elements (new XName ("user")).Element (new XName ("info")).Value;

even for simplest queries, would LINQ be totally worth it for complex ones?

Another important issue here is that XNames are atomized. See MSDN:

XName objects are guaranteed to be atomized; that is, if two XName objects have exactly the same namespace and exactly the same local name, they will share the same instance. The equality and comparison operators are also provided explicitly for this purpose.

Among other benefits, this feature allows for faster execution of queries. When filtering on the name of elements or attributes, the comparisons expressed in predicates use identity comparison, not value comparison. It is much faster to determine that two references actually refer to the same object than to compare two strings.

You can't provide atomization in constructor, but defining a conversion allows you to pick corresponding object from the pool and return it as if it were a new instance.

gaearon