Lesser known C# features – Part 2

This is Part 2 of the series, lesser-known features of C# language. I would suggest you to go through Part 1 if you have not already.

ObsoleteAttribute

From my experience, Obsolete attribute is more of lesser used than lesser known. As the name suggests, this attribute is used for marking elements such methods, properties, classes etc as deprecated. This attribute can be used when you are working on a class library or a NuGet package which used by internal or external users and you want your users to no longer use an old version of class/ method/ property.  This gives time to your users to make changes in their code without breaking their application. For example, consider the below Logger class


public static class Logger
{
public static void Log(string message,
[CallerFilePath] string sourceFilePath = "",
[CallerLineNumber] int sourceLineNumber = 0,
[CallerMemberName] string callerMemberName = "")
{
Log($"[Message]: {message}; [Source File Path]: {sourceFilePath}; " +
$"[Source Line Number]: {sourceLineNumber}; [Caller Member Name]: {callerMemberName}; ");
}
[Obsolete("This method is will be removed in the future. Use Log(string message) instead.")]
public static void Log(string message, string callerMemberName)
{
Log($"[Message]: {message}; [Caller Member Name]: {callerMemberName}; ");
}
private static void Log(string message)
{
Debug.WriteLine(message);
}
}

In the above code, one of the overloads of the Log method is marked as Obsolete. When a caller invokes this method, it gets the below warning:

ObsoleteAttributeExample

Additionally, if the calling project treats “warning as error” then the developers will get compile time error when they try to use this method. This will force the user calling the logger method to either update the code to use the new implementation of Log method or disable the warning explicitly.

ObsoleteAttributeExampleDisableWarning.png

char.GetUnicodeCategory

Type char has a number of helper methods like IsDigitIsLetterIsLower etc. In addition to this, char also exposes a method called GetUnicodeCategory  which returns enum UnicodeCategory.

As per unicode.org

The Unicode Standard provides a unique number for every character, no matter what platform, device, application or language.

The UnicodeCategory categorizes the input character into category such as CurrencySymbol, ClosePunctuation, LineSeparator, LowercaseLetter etc. This information may be useful for different scenarios like when you need to validate a user input string. Example:


using System;
namespace GetUnicodeCategoryExample
{
class Program
{
static void Main(string[] args)
{
var sampleString = @"1aB%$^()-";
for (int i = 0; i < sampleString.Length; i++)
{
Console.WriteLine($"[Char]: {sampleString[i]};" +
$" [UnicodeCategory]: {char.GetUnicodeCategory(sampleString[i])}"); // or char.GetUnicodeCategory(sampleString, i)
}
Console.Read();
}
}
}

The output of the above code is:

[Char]: 1; [UnicodeCategory]: DecimalDigitNumber
[Char]: a; [UnicodeCategory]: LowercaseLetter
[Char]: B; [UnicodeCategory]: UppercaseLetter
[Char]: %; [UnicodeCategory]: OtherPunctuation
[Char]: $; [UnicodeCategory]: CurrencySymbol
[Char]: ^; [UnicodeCategory]: ModifierSymbol
[Char]: (; [UnicodeCategory]: OpenPunctuation
[Char]: ); [UnicodeCategory]: ClosePunctuation
[Char]: -; [UnicodeCategory]: DashPunctuation

ConditionalAttribute

You might be already aware of conditional preprocessor directives like #if#elif#endif. Preprocessors directive help in doing a conditional compilation.

ConditionalAttribute provides an elegant and clean alternative to the directives. Consider the slight variation of Logger class in the example below. We now have an additional method called DebugLog with ConditionalAttrbute.


using System.Diagnostics;
using System.Runtime.CompilerServices;
namespace ConditionalAttributeExample
{
public static class Logger
{
public static void Log(string message,
[CallerFilePath] string sourceFilePath = "",
[CallerLineNumber] int sourceLineNumber = 0,
[CallerMemberName] string callerMemberName = "")
{
Log($"[Message]: {message}; [Source File Path]: {sourceFilePath}; " +
$"[Source Line Number]: {sourceLineNumber}; [Caller Member Name]: {callerMemberName}; ");
}
[Conditional("DEBUG")]
public static void DebugLog(string message, [CallerMemberName] string callerMemberName = "")
{
Log($"[Message]: {message}; [Caller Member Name]: {callerMemberName};");
}
private static void Log(string message)
{
Debug.WriteLine(message);
}
}
}

view raw

Logger.cs

hosted with ❤ by GitHub


namespace ConditionalAttributeExample
{
class Program
{
static void Main(string[] args)
{
Logger.DebugLog("Member Start");
// Perform operation
Logger.Log("Testing logging inside Main() method");
Logger.DebugLog("Member End");
}
}
}

view raw

Main.cs

hosted with ❤ by GitHub

The output of the above code is self-explanatory. The DebugLog method will only be executed when we are running this code in Debug mode/ configuration. With the help ofConditionalAttribute, however, our Main method remains free of the preprocessor or ugly if else conditions.

Please note that you can also define your own conditional compilation symbols under Project Properties -> Build as shown in the figure below:

CustomCompilationSymbol.PNG

Note: The conditional attribute and preprocessor directives are not the same. as mentioned in one of the answers of this MSDN post. With conditional attribute, the method is still part of the assembly. However, with preprocessor directive the piece of code is not visible to assembly itself (when the condition is false).

Hope these tips help you write better code and improving your programming skills. Please share your view and feedback in the comments section below.

Thank you for taking time to read this post. Part 3 is on the way soon! 🙂


Posted

in

by

Tags:

Comments

2 responses to “Lesser known C# features – Part 2”

  1. […] Lesser known C# – feature – Part 2 […]

    Like

  2. […] Lesser known C# features – Part 2 […]

    Like

Leave a Reply

A WordPress.com Website.