C# – Preprocessor Directives

Preprocessor Directives

Preprocessor directives supply the compiler with additional information about regions of code.

The most common preprocessor directives are the conditional directives, which provide a way to include or exclude regions of code from compilation.

In this class, the statement in Foo is compiled as conditionally dependent upon the presence of the DEBUG symbol.

#define DEBUG
class MyClass
{
  int x;
  void Foo()
  {
     #if DEBUG
     Console.WriteLine("Testing: x = {0}", x);
     #endif
  }
  ...
}
#define symbol Defines symbol
#undef symbol Undefines symbol
#if symbol [operator symbol2]… symbol to test
operators are ==, !=, &&, and || followed by #else, #elif, and #endif
#else Executes code to subsequent #endif
#elif symbol [operator symbol2] Combines #else branch and #if test
#endif Ends conditional directives
#warning text text of the warning to appear in compiler output
#error text text of the error to appear in compiler output
#pragma warning [disable | restore] Disables/restores compiler warning(s)
#line [ number [“file”] | hidden] number specifies the line in source code; file is the filename to appear in computer output; hidden instructs debuggers to skip over code from this point until the next #line directive.
#region name Marks the beginning of an outline
#endregion Ends an outline region

Conditional Attributes

An attribute decorated with the Conditional attribute will be compiled only if a given preprocessor symbol is present.

//file1.cs
#define DEBUG
using System;
using System.Diagnostics;
[Conditional("DEBUG")]
public class TestAttribute : Attribute {}

//file2.cs
#define DEBUG
[Test]
class Foo
{
  [Test]
  string s;
}

The compiler will only incorporate the [Test] attributes if the DEBUG symbol is in
scope for file2.cs.

Pragma Warning

The compiler generates a warning when it spots something in your code that seems
unintentional but it does not prevent the code from compiling.

Compiler warnings can be extremely valuable in spotting bugs.

Pragma Warning is used to suppress warning. Omitting the number in the #pragma warning directive disables or restores all warning codes.

public class Foo
{
	static void Main() {}

	#pragma warning disable 414
	static string Message = "Hello";
	#pragma warning restore 414
}

C# – Unsafe Code and Pointers

Unsafe Code and Pointers

C# supports direct memory manipulation via pointers within blocks of code marked unsafe and compiled with the /unsafe compiler option.

Pointer Basics

A pointer instance holds the address of a variable. Pointer types can be (unsafely) cast to any other pointer type.

& The address-of operator returns a pointer to the address of a variable
* The dereference operator returns the variable at the address of a pointer
-> The pointer-to-member operator is a syntactic shortcut, in which x->y is equivalent to (*x).y

Unsafe Code

Using the keyword unsafe for a type, type member or statement block, you are permitted to use pointer types and perform C++ style pointer operations on memory within that scope.

// C# supports direct memory manipulation via pointers within blocks of code marked unsafe
// and compiled with the /unsafe compiler option. LINQPad implicitly compiles with this option.

// Here's how to use pointers to quickly process a bitmap:

unsafe static void BlueFilter (int[,] bitmap)
{
  int length = bitmap.Length;
  fixed (int* b = bitmap)
  {
	int* p = b;
	for (int i = 0; i < length; i++)
	  *p++ &= 0xFF;
  }
}

static void Main()
{
	int[,] bitmap = { {0x101010, 0x808080, 0xFFFFFF}, {0x101010, 0x808080, 0xFFFFFF} };
	BlueFilter (bitmap);
	bitmap.Dump();
}

The Fixed Statement

The fixed statement is required to pin a managed object so that the garbage collector can pin the object and not move it around.

Within a fixed statement, you can get a pointer to any value type, an array of value types, or a string. In the case of arrays and strings, the pointer will actually point to the first element, which is a value type.

// Value types declared inline within reference types require the reference type to be pinned:

class Test
{
	public int X;
}

static void Main()
{
	Test test = new Test();
	unsafe
	{
	   fixed (int* p = &test.X)   // Pins test
	   {
		 *p = 9;
	   }
	   Console.WriteLine (test.X);
	}	
}

The Pointer-to-Member Operator

// In addition to the & and * operators, C# also provides the C++ style -> operator,
// which can be used on structs:

struct Test
{
	public int X;
}

unsafe static void Main()
{
	Test test = new Test();
	Test* p = &test;
	p->X = 9;
	Console.WriteLine (test.X);
}

Arrays

The stackalloc keyword

// Memory can be allocated in a block on the stack explicitly using the stackalloc keyword:

unsafe
{
	int* a = stackalloc int [10];
	for (int i = 0; i < 10; ++i)
		Console.WriteLine (a[i]);   // Print raw memory
}

Fixed-size buffers

// Memory can be allocated in a block within a struct using the fixed keyword:

unsafe struct UnsafeUnicodeString
{
	public short Length;
	public fixed byte Buffer[30];
}

unsafe class UnsafeClass
{
	UnsafeUnicodeString uus;
	public UnsafeClass (string s)
	{
		uus.Length = (short)s.Length;
		fixed (byte* p = uus.Buffer)
		for (int i = 0; i < s.Length; i++)
			p[i] = (byte) s[i];
	}
}

static void Main() { new UnsafeClass ("Christian Troy"); }

void*

// A void pointer (void*) makes no assumptions about the type of the underlying data and is
// useful for functions that deal with raw memory:
//A void* cannot be dereferenced, and arithmetic operations cannot be performed on void pointers.
unsafe static void Main()
{
	short[] a = {1,1,2,3,5,8,13,21,34,55};
	fixed (short* p = a)
	{
		//sizeof returns size of value-type in bytes
		Zap (p, a.Length * sizeof (short));
	}
	foreach (short x in a)
		System.Console.WriteLine (x);   // Prints all zeros
}

unsafe static void Zap (void* memory, int byteCount)
{
	byte* b = (byte*) memory;
		for (int i = 0; i < byteCount; i++)
			*b++ = 0;
}

C# – Attributes

Attributes

Attributes are an extensible mechanism for adding custom information to code elements (assemblies, types, members, return values, parameters, and generic type parameters).

This extensibility is useful for services that integrate deeply into the type system, without requiring special keywords or constructs in the C# language.

Example:
serialization – the process of converting arbitrary objects to and from a particular format.

Attribute Classes

Attribute defined by a class inherits from 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.

[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.

Named and Positional Attribute Parameters

Attributes may have parameters.

The following attribute maps the CustomerEntity class to an XML element named Customer, belonging to the http://oreilly.com namespace.

[XmlElement ("Customer", Namespace="http://oreilly.com")]
public class CustomerEntity { ... }

Attribute parameters fall into one of two categories: positional or named. The first argument is a positional parameter and second is a named parameter.

Positional parameters correspond to parameters of the attribute type’s public constructors. You must include positional parameters that correspond to one of the attribute’s constructors.

Named parameters correspond to public fields or public properties on the attribute type and it is optional.

Attribute Targets

Implicitly, the target of an attribute is the code element it immediately precedes, which is typically a type or type member.

Example of using the CLSCompliant attribute to specify CLS compliance for an entire assembly.

[assembly:CLSCompliant(true)]

Specifying Multiple Attributes

Multiple attributes can be specified for a single code element.

Each attribute can be listed either within the same pair of square brackets (separated by a comma) or in separate pairs of square brackets (or a combination of the two).

[Serializable, Obsolete, CLSCompliant(false)]
public class Bar {...}
[Serializable] [Obsolete] [CLSCompliant(false)]
public class Bar {...}
[Serializable, Obsolete]
[CLSCompliant(false)]
public class Bar {...}

Caller Info Attributes (C# 5)

From C# 5, you can tag optional parameters with one of three caller info attributes, which instruct the compiler to feed information obtained from the caller’s source code into the parameter’s default value:

[CallerMemberName] applies the caller’s member name

[CallerFilePath] applies the path to caller’s source code file

[CallerLineNumber] applies the line number in caller’s source code file

using System.Runtime.CompilerServices;

namespace Rextester
{
    public class Program
    {
        public static void Main(string[] args)
        {
           Foo();
        }
        
        static void Foo (
	[CallerMemberName] string memberName = null,
	[CallerFilePath] string filePath = null,
	[CallerLineNumber] int lineNumber = 0)
{
	Console.WriteLine (memberName);
	Console.WriteLine (filePath);
	Console.WriteLine (lineNumber);
}
    }   
   
}

Output:
Main
c:\Windows\Temp\dlexntz1.0.cs
16

Caller info attributes are useful for logging—and for implementing patterns such as firing a single change notification event whenever any property on an object changes.

INotifyPropertyChanged in System.ComponentModel

public interface INotifyPropertyChanged
{
    event PropertyChangedEventHandler PropertyChanged;
}
public delegate void PropertyChangedEventHandler
(object sender, PropertyChangedEventArgs e);

public class PropertyChangedEventArgs : EventArgs
{
    public PropertyChangedEventArgs (string propertyName);
    public virtual string PropertyName { get; }
}

By applying the [CallerMemberName] attribute, however, it implement this interface and invoke the event without ever specifying property names

public class Foo : INotifyPropertyChanged
{
   public event PropertyChangedEventHandler PropertyChanged = delegate { };
   void RaisePropertyChanged ([CallerMemberName] string propertyName = null)
   {
      PropertyChanged (this, new PropertyChangedEventArgs (propertyName));
   }
   string customerName;
   public string CustomerName
   {
      get { return customerName; }
      set
      {
          if (value == customerName) return;
          customerName = value;
          RaisePropertyChanged();
          // The compiler converts the above line to:
          // RaisePropertyChanged ("CustomerName");
      }
   }
}

C# – Anonymous Types

Anonymous Types

Anonymous type is a simple class created by the compiler on the fly to store a set of values.

It is created using the new keyword followed by an object initializer, specifying the properties and values.

You must use the var keyword to reference an anonymous type.

Anonymous types are used mostly when writing LINQ queries.

var dude = new {Name = "Bob", Age = 23};

compiler compiles into:

internal class AnonymousGeneratedTypeName
{
	private string name; // Actual field name is irrelevant
	private int age; // Actual field name is irrelevant

	public AnonymousGeneratedTypeName (string name, int age)
	{
		this.name = name; this.age = age;
	}

	public string Name { get { return name; } }

	public int Age { get { return age; } }


	// The Equals and GetHashCode methods are overridden (see Chapter 6).
	// The ToString method is also overridden.
}
...
var dude = new AnonymousGeneratedTypeName ("Bob", 23);

The property name of an anonymous type can be inferred from an expression that is itself an identifier (or ends with one).

int Age = 23;

// The following:
{
	var dude = new { Name = "Bob", Age, Age.ToString().Length };
	dude.Dump();
}
// is shorthand for:
{
	var dude = new { Name = "Bob", Age = Age, Length = Age.ToString().Length };
	dude.Dump();
}

Two anonymous type instances will have the same underlying type if their elements are same-typed and they’re declared within the same assembly.

var a1 = new { X = 2, Y = 4 };
var a2 = new { X = 2, Y = 4 };
Console.WriteLine (a1.GetType() == a2.GetType());   // True

// Additionally, the Equals method is overridden to perform equality comparisons:

Console.WriteLine (a1 == a2);         // False
Console.WriteLine (a1.Equals (a2));   // True

You can create arrays of anonymous types

var dudes = new[]
{
   new {Name = "Bob", Age = 30},
   new {Name = "Tom", Age = 40}
}