Hidden Features of C#
1. ObsoleteAttribute
ObsoleteAttribute applies to all program elements except assemblies, modules, parameters, and return values. Marking an element as obsolete informs users that the element will be removed in future versions of the product.Message property contains a string that will be displayed when the attribute assignee is used. It is recommended a workaround to be provided in this description.
IsError– If set to true the compiler will indicate an error if the attribute target is used in the code.
If we use the above class in our code, an error, and a warning are going to be displayed.
Hide Copy Code
Console.WriteLine(ObsoleteExample.OrderDetailTotal);
Console.WriteLine();
Console.WriteLine(ObsoleteExample.CalculateOrderDetailTotal());
2. Setting a default value for C# Auto-implemented properties via DefaultValueAttribute
DefaultValueAttribute specifies the default value for a property. You can create a DefaultValueAttribute with any value. A member’s default value is typically its initial value.The attribute won’t cause a member to be automatically initialized with the specified value. Hence, you must set the initial value in your code.
The auto-implemented properties are initialized in the constructor of the class via reflection. The code iterates through all properties of the class and set them their default value if the DefaultValueAttribute is present.
Official documentation– https://msdn.microsoft.com/en-us/library/system.componentmodel.defaultvalueattribute.aspx
3. DebuggerBrowsableAttribute
Determines if and how a member is displayed in the debugger variable windows.If you use the sample class in your code and try to step through it via the debugger (F11), you will notice that the code is just executing.
Hide Copy Code
DebuggerBrowsableTest.SquirrelFirstNameName = "Hammy";
DebuggerBrowsableTest.SquirrelLastNameName = "Ammy";
Official documentation– https://msdn.microsoft.com/en-us/library/system.diagnostics.debuggerbrowsableattribute.aspx4. ?? Operator
One of my favorite “hidden features of C#” is the ?? operator. I’m using it heavily in my code.The ?? operator returns the left-hand operand if it is not null, or else it returns the right operand. A nullable type can contain a value, or it can be undefined. The ?? operator defines the default value to be returned when a nullable type is assigned to a non-nullable type.
int? x = null;
int y = x ?? -1;
Console.WriteLine("y now equals -1 because x was null => {0}", y);
int i = DefaultValueOperatorTest.GetNullableInt() ?? default(int);
Console.WriteLine("i equals now 0 because GetNullableInt() returned null => {0}", i);
string s = DefaultValueOperatorTest.GetStringValue();
Console.WriteLine("Returns 'Unspecified' because s is null => {0}", s ?? "Unspecified");
Official documentation– https://msdn.microsoft.com/en-us/library/ms173224(v=vs.80).aspx5. Curry and Partial methods
Curry– In mathematics and computer science, currying is the technique of translating the evaluation of a function that takes multiple arguments (or a tuple of arguments) into evaluating a sequence of functions, each with a single argument.In order to be implemented via C#, the power of the extension methods is used.
public static class CurryMethodExtensions
{
public static Func<A, Func<B, Func<C, R>>> Curry<A, B, C, R>(this Func<A, B, C, R> f)
{
return a => b => c => f(a, b, c);
}
}
The curry extension method usage is a little bit overwhelming at first.
Hide Copy Code
Func<int, int, int, int> addNumbers = (x, y, z) => x + y + z;
var f1 = addNumbers.Curry();
Func<int, Func<int, int>> f2 = f1(3);
Func<int, int> f3 = f2(4);
Console.WriteLine(f3(5));
The types returned by the different methods can be exchanged with the var keyword.Official documentation– https://en.wikipedia.org/wiki/Currying#/Contrast_with_partial_function_application
Partial – in computer science, partial application (or partial function application) refers to the process of fixing a number of arguments to a function, producing another function of smaller arity.
public static class CurryMethodExtensions
{
public static Func<C, R> Partial<A, B, C, R>(this Func<A, B, C, R> f, A a, B b)
{
return c => f(a, b, c);
}
}
The partial extension method usage is more straightforward than the curry one.
Hide Copy Code
Func<int, int, int, int> sumNumbers = (x, y, z) => x + y + z;
Func<int, int> f4 = sumNumbers.Partial(3, 4);
Console.WriteLine(f4(5));
Again the types of the delegates can be declared with the var keyword.Official documentation– https://en.wikipedia.org/wiki/Partial_application
6. WeakReference
A weak reference allows the garbage collector to collect an object while still allowing an application to access the object. If you need the object, you can still obtain a strong reference to it and prevent it from being collected.WeakReferenceTest hugeObject = new WeakReferenceTest();
hugeObject.SharkFirstName = "Sharky";
WeakReference w = new WeakReference(hugeObject);
hugeObject = null;
GC.Collect();
Console.WriteLine((w.Target as WeakReferenceTest).SharkFirstName);
If the garbage collector is not called explicitly, there will be significant chance the weak reference to be still assigned.Official documentation– https://msdn.microsoft.com/en-us/library/system.weakreference.aspx
7. Lazy<T>
Use lazy initialization to defer the creation of a large or resource-intensive object, or the execution of a resource-intensive task, particularly when such creation or execution might not occur during the lifetime of the program.public abstract class ThreadSafeLazyBaseSingleton<T>
where T : new()
{
private static readonly Lazy<T> lazy = new Lazy<T>(() => new T());
public static T Instance
{
get
{
return lazy.Value;
}
}
}
Official documentation– https://msdn.microsoft.com/en-us/library/dd642331(v=vs.110).aspx8. BigInteger
The BigInteger type is an immutable type that represents an arbitrarily large integer whose value, in theory, has no upper or lower bounds. This type differs from the other integral types in the .NET Framework, which have a range indicated by their MinValue and MaxValue properties.Note: Because the BigInteger type is immutable and because it has no upper or lower bounds, an OutOfMemoryException can be thrown for any operation that causes a BigInteger value to grow too large.
Official documentation– https://msdn.microsoft.com/en-us/library/system.numerics.biginteger(v=vs.110).aspx
9. Undocumented C# Keywords __arglist __reftype __makeref __refvalue
I’m not sure that these can be treated as hidden features of C# because they are undocumented, and you should be careful with them. Probably there isn’t a documentation for a reason. Maybe they are not adequately tested. However, they are colored by the Visual Studio editor and recognized as official keywords.You can create a typed reference from a variable by using the __makeref keyword. The original type of the variable represented by the typed reference can be extracted using the __reftype keyword. Lastly, the value can be obtained from the TypedReference using the __refvalue keyword. The __arglist has similar behavior to the keyword params- you can access parameters lists.
int i = 21;
TypedReference tr = __makeref(i);
Type t = __reftype(tr);
Console.WriteLine(t.ToString());
int rv = __refvalue( tr,int);
Console.WriteLine(rv);
ArglistTest.DisplayNumbersOnConsole(__arglist(1, 2, 3, 5, 6));
In order to be able to use __arglist you need the ArglistTest class.
Hide Copy Code
public static class ArglistTest { public static void DisplayNumbersOnConsole(__arglist) { ArgIterator ai = new ArgIterator(__arglist); while (ai.GetRemainingCount() > 0) { TypedReference tr = ai.GetNextArg(); Console.WriteLine(TypedReference.ToObject(tr)); } } }Remarks the ArgIterator object enumerates the argument list starting from the first optional argument, this constructor is provided specifically for use with the C/C++ programming language.
Reference– http://www.nullskull.com/articles/20030114.asp and http://community.bartdesmet.net/blogs/bart/archive/2006/09/28/4473.aspx
10. Environment.NewLine
Gets the newline string defined for this environment.Console.WriteLine("NewLine: {0} first line{0} second line{0} third line", Environment.NewLine);
Official documentation– https://msdn.microsoft.com/en-us/library/system.environment.newline(v=vs.110).aspx11. ExceptionDispatchInfo
Represents an exception whose state is captured at a certain point in code. You can use the ExceptionDispatchInfo.Throw method, which can be found in the System.Runtime.ExceptionServices namespace. This method can be used to throw an exception and preserve the original stack trace.The caught exception can be thrown again in another method or even in another thread.
Official documentation– https://msdn.microsoft.com/en-us/library/system.runtime.exceptionservices.exceptiondispatchinfo(v=vs.110).aspx
If you want to exit your program without calling any finally blocks or finalizers use FailFast.
string s = Console.ReadLine();
try
{
int i = int.Parse(s);
if (i == 42) Environment.FailFast("Special number entered");
}
finally
{
Console.WriteLine("Program complete.");
}
If i equals 42 the finally block won’t be executed.Official documentation– https://msdn.microsoft.com/en-us/library/ms131100(v=vs.110).aspx
13. Debug.Assert & Debug.WriteIf & Debug.Indent
Debug.Assert– checks for a condition; if the condition is false, outputs messages and displays a message box that shows the call stack.Debug.Assert(1 == 0, "The numbers are not equal! Oh my god!");
If the assert fails in Debug mode, the below alert is displayed, containing the specified message.Debug.WriteLineIf(1 == 1, "This message is going to be displayed in the Debug output! =)");
Debug.Indent/Debug.Unindent– increases the current IndentLevel by one.
Hide Copy Code
Debug.WriteLine("What are ingredients to bake a cake?"); Debug.Indent(); Debug.WriteLine("1. 1 cup (2 sticks) butter, at room temperature."); Debug.WriteLine("2 cups sugar"); Debug.WriteLine("3 cups sifted self-rising flour"); Debug.WriteLine("4 eggs"); Debug.WriteLine("1 cup milk"); Debug.WriteLine("1 teaspoon pure vanilla extract"); Debug.Unindent(); Debug.WriteLine("End of list");If we want to display the ingredients for a cake in the Debug Output Window, we can use the code above.
14. Parallel.For & Parallel.Foreach
I’m not sure if we can add these to the hidden features of C# list because they are heavily used in the TPL (Task Parallel Library). However, I’m listing them here because I like them a lot and utilize their power in my multithreaded applications.Parallel.For– executes a for loop in which iterations may run in parallel.
Interlocked.Add method adds two integers and replaces the first integer with the sum, as an atomic operation.
Parallel.Foreach– executes a foreach (For Each in Visual Basic) operation in which iterations may run in parallel.
Official documentation: Parallel.For and Parallel.Foreach
Returns a value indicating whether the specified number evaluates to negative or positive infinity.
Console.WriteLine("IsInfinity(3.0 / 0) == {0}.", Double.IsInfinity(3.0 / 0) ? "true" : "false");
Official documentation– https://msdn.microsoft.com/en-us/library/system.double.isinfinity(v=vs.110).aspxSource Code
You can download the full source code from my Github Repository.
You can check the next part of the series - Top 15 Hidden Features of C# Part 2
License
This article, along with any associated source code and files, is licensed under The Microsoft Public License (Ms-PL)