Extension methods allow an existing type to be extended with new methods without altering the definition of the original type. An extension method is a static method of a static class, where the this modifier is applied to the first parameter. The type of the first parameter will be the type that is extended. For example:
public static class StringHelper { public static bool IsCapitalized (this string s) { if (string.IsNullOrEmpty(s)) return false; return char.IsUpper(s[0]); } }
TheIsCapitalizedextension method can be called as though it were an instance method on a string, as follows:
Console.WriteLine("Perth".IsCapitalized());
An extension method call, when compiled, is translated back into an ordinary static method call:
An extension method cannot be accessed unless the namespace is in scope. Consider the extension method IsCapitalized in the following example:
IsCapitalized in the following example:An extension method cannot be accessed unless the namespace is in scope. Consider the extension method IsCapitalized in the following example:
using System;
namespace Utils { public static class StringHelper { public static bool IsCapitalized (this string s) { if (string.IsNullOrEmpty(s)) return false; return char.IsUpper(s[0]); } } }
To useIsCapitalized, the following application must importUtils, in order to avoid a compile-time error:
namespace MyApp { using Utils;
class Test { static void Main() { Console.WriteLine("Perth".IsCapitalized()); } } }
Extension methods versus instance methods
Any compatible instance method will always take precedence over an extension method. In the following example, Test’s Foo method will always take precedence—even when called with an argument x of type int:
class Test { public void Foo (object x) { } // This method always wins }
static class Extensions { public static void Foo (this Test t, int x) { } }
The only way to call the extension method in this case is via normal static syntax; in other words,Extensions.Foo(...).
Extension methods versus extension methods
If two extension methods have the same signature, the extension method must be called as an ordinary static method to disambiguate the method to call. If one extension method has more specific arguments than another, the more specific extension method takes precedence over the less one. For example:
static class StringHelper { public static bool IsCapitalized (this string s) { if (string.IsNullOrEmpty (s)) return false; return char.IsUpper (s[0]); } }
static class ObjectHelper { public static bool IsCapitalized (this object s) { return true; } }
An anonymous type is a simple class created on the fly to store a set of values. To create an anonymous type, use the new keyword followed by an object initializer, specifying the properties and values the type will contain. For example:
var dude = new { Name = "Bob", Age = 1 };
The compiler translates this to the following:
internal class AnonymousGeneratedTypeName { private string name; // actual field name is irrelevant private int age; // actual field name is irrelevant
public string Name get {return name;} {set {name = value;}} public int Age get {return age; } {set {age = value; }} } ...
You’re already familiar with the notion of attributing code elements of a program with modifiers, such as virtual or ref. These constructs are built into the language. Attributes are an extensible mechanism for adding custom information to code elements (assemblies, types, members, return values, and parameters). This extensibility is useful for services that integrate deeply into the type system, without requiring special keywords or constructs in the C# language.
A good scenario for attributes is serialization—the process of converting arbitrary objects to and from a particular format. In this scenario, an attribute on a field can specify the translation between C#'s representation of the field and the format’s representation of the field.
Attribute Classes
An attribute is defined by a class that inherits (directly or indirectly) from the abstract class System.Attribute. To attach an attribute to a code element, specify the attribute’s type name in square brackets, before the code element. For example, the following attaches the ObsoleteAttribute to the Foo class:
[ObsoleteAttribute] public class Foo {...}
This attribute is recognized by the compiler and will cause compiler warnings if a type or member marked obsolete is referenced. By convention, all attribute types end in the word “Attribute”. C# recognizes this and allows you to omit the suffix when attaching an attribute:
[Obsolete] public class Foo {...}
ObsoleteAttributeis a type declared in theSystemnamespace as follows (simplified for brevity):
public sealed class SerializableAttribute : Attribute {...}
The C# language and the .NET Framework include a number of predefined attributes. We describe how to write your own attributes in Chapter 17.
Named and Positional Parameters
Attributes may have parameters. In the following example, we apply the XmlElement attribute to a class. The XmlElement attribute tells the System.Xml.Linq model how an object is represented in XML. The XmlElement attribute accepts several attribute parameters. The following attribute maps the CustomerEntityclass to an XML element namedCustomer, belonging to the http://oreilly.com namespace:
[XmlElement ("Customer", Namespace=http://blah)] public class CustomerEntity { ... }
Attribute parameters fall into one of two categories: positional and named. In the preceding example, the first argument is a positional parameter; the second is a named parameter. Positional parameters correspond to parameters of the attribute type’s public constructors. Named parameters correspond to public fields or public properties on the attribute type.
When specifying an attribute, you must include positional parameters that correspond to one of the attribute’s constructors. Named parameters are optional.
In Chapter 17, we describe the valid parameter types and rules for their evaluation.
Please check back next week for the continuation of this article.