C# Tips that improve code efficiency and productivity
1. Ternary Operator (?:)
Ternary operator. The ternary operator tests a condition. It compares 2 values. It produces a third value that depends on the result of the comparison.The ternary effect can be accomplished with if-statements or other constructs. The ternary operator provides an elegant and equivalent syntax form to the if-statement.
Example. One use of a ternary is to initialize a variable with the result of the expression. The C# compiler translates the ternary expression into branch statements such as brtrue. IL However: The high-level ternary statement is useful because it allows you to condense multiple if-statements and reduce nesting.
Tip: It does not actually eliminate branching. It simplifies the high-level representation of the branching.
Based on: .NET 4.6
Based on: .NET 4.6
C# program that uses ternary operator using System; class Program { static void Main() { // // If the expression is true, set value to 1. // Otherwise, set value to -1. // int value = 100.ToString() == "100" ? 1 : -1; Console.WriteLine(value); } } Output 1
Notes, above program. If the subexpression evaluates to true, the integer variable with the identifier "value" has its location on the stack copied to the bit values of the integer 1. Otherwise: It has its location on the stack copied to the binary representation of -1.
Internally: This code uses branch statements that are defined in the intermediate language.
Return. You can return the result of a ternary statement. The result of evaluating the ternary operator must match the return type of the enclosing method for the compilation to succeed. Note: The intermediate language generated is equivalent to an if-statement, but the source code contains fewer lines of code.
C# program that returns ternary expression using System; class Program { static void Main() { Console.WriteLine(GetValue("Sam")); Console.WriteLine(GetValue("Jane")); } /// <summary> /// Return the value 100 if the name matches, otherwise -1. /// </summary> static int GetValue(string name) { return name == "Sam" ? 100 : -1; } } Output 100 -1
Notes, above program. The GetValue method internally executes the intermediate language branching instructions that are equivalent to the logic expressed in the high-level ternary operator. And: If the parameter to GetValue is equal to the string data in the literal, the integer 100 is returned. Otherwise -1 is returned.
Implicit conversion error. The two result values of a ternary must have implicit conversions to the same type. Here we cannot cast a string to an int in a ternary, so we get an error. Tip: To fix ternaries with this error, try adding casts to the 2 result values so they have the same type.
Casts
C# program that causes implicit conversion error class Program { static void Main() { int temp = 200; int value = temp == 200 ? "bird" : 0; } } ResultsError CS0173Type of conditional expression cannot be determined because there is no implicit conversion between 'string' and 'int'
2. Null-Coalesce Operator (??)
Every time we need to test for null values in our code, the null-coalesce operator (??) becomes handy to use. I’ve created 3 code-snippets below, just to demonstrate code efficiency and to show how much shorter the code can become.2.1 The regular newbie way of testing for null value
object a = null; object b = new object(); object c; if (a != null) c = a; else c = b;It is very obvious that a C# developer that has just learned to use the ternary operator will rewrite this to a single line.
2.2 Using the ternary operator to test null
object a = null; object b = new object(); object c = (a != null) ? a : b;With the null-coalescing operator we can make this shorter. If the left-hand side equals null, the right-hand side will be assigned, in this context object b.
2.3 Using the null-coalescing operator to test null
object a = null; object b = new object(); object c = a ?? b;
3. Boolean
The Boolean (a.k.a. bool) is a data type that supports two states, true/false. The bool data type is commonly used to determine whether a return value is true or false, as we saw with the third example in the examples that use the ternary operator. To explain what code efficiency one can gain by just using bool in if-statements and return values, I’ve written some examples below.3.1 If-else statement
To be more correct, and more efficient one does not need to fix check whether a method will be true or false, only invoke the method name inside the if-statement, and the rest of the check will occur automatically.
if (IndividualsNormalAge() == true) { System.Diagnostics.Debug.Print("Yes!"); } else { System.Diagnostics.Debug.Print("No!"); }To make this more efficient, simply remove == true, and it will be determined autormatically.
if (IndividualsNormalAge()) { System.Diagnostics.Debug.Print("Yes!"); } else { System.Diagnostics.Debug.Print("No!"); }3.2 Return
In some cases one does not need to use the ternary operator to return true/false, that’s how simple it is, below I have a few code-snippets:
bool IndividualsNormalAge() { Random r = new Random(); int age = r.Next(1, 113); return (age < 111) ? true : false; }The ? true : false can be removed, and it will still return true/false.
bool IndividualsNormalAge() { Random r = new Random(); int age = r.Next(1, 113); return (age < 111); }It is very easy to understand this, to clarify further, I've added two more code-snippets below:
return (i == 1) ? true : false;and even shorter:
return (i == 1); // Returns true only if i equals 1
4. Is String Null?
The string.IsNullOrEmpty tests strings by checking for string references that are null, or empty strings. In short, the static method IsNullOrEmpty enables you to simultaneously test whether a String is null or its value is Empty—as tested in the code-snippet below:if (string.IsNullOrEmpty(s)) return "This string is null or empty."; else return string.Format("This string is not null or empty, it equals to \"{0}\" ", s);
5. Data Type Conversion
It's very common that we sometimes or very often have to convert data types for various of reasons, for instance, if the we have a decimal variable with a set value, and we want to convert it into an Integer or int an explicit conversion will be performed to achieve this.int j = 0; decimal money = 9500.34m; j = (int)money; // An explicit conversion using cast operator.The better option is to use the Convert class which supports full Data Type conversion between all data types.
int money = 0; string uservalue = null; uservalue = Console.ReadLine(); money = Convert.ToInt32(uservalue);In the first code-snippet we do explicit conversion using a cast operator, and in the second code-snippet we use the Convert class and invoke the ToInt32() method to convert a string to an int. Is there any difference? Yes, of course there's a difference between the first code-snippet and the second code-snippet—I'll explain, explicit conversions strictly requires a cast operator, and that the source and destination variables are compatible. Conversion using a helper class such as Convert allows us to convert between non-compatible types and does not require the use of cast operator, thus, providing a safe conversion method with performance benefits.
6. Using Statement
Allocation of memory is as important as freeing memory. However, in C# we have the Garbage Collector (GC) which takes care of a lot, but some classes in the .NET library implement the IDisposable interface,and these require manual object disposal. In the code-snippet below, we make a new instance of the SHA1 class, and in the finally code-block we dispose the object.
SHA1 sha1 = SHA1.Create(); try { StringBuilder sb = new StringBuilder(); byte[] data = Encoding.UTF8.GetBytes(text); byte[] hash = sha1.ComputeHash(data); foreach (byte b in hash) { sb.Append(b.ToString("x2").ToLower()); } hashed = sb.ToString(); } finally { if (sha1 != null) ((IDisposable)sha1).Dispose(); }A better way is to use the using statement once, as shown below:
// Allocate using (System.Security.Cryptography.SHA1 sha1 = System.Security.Cryptography.SHA1.Create()) { //Use the sha1 to computer the hash. //sha1 can only be used inside this code-block } //Automatically DisposedIn the second code-snippet, using (...) { ... } will only allow the usage of an object once within the curly braces ("{}"), and disposal will happen automatically after the last curly brace. This approach is much better, especially, if we only need to use one class once.
7. Properties
Prior to C# 2.0, programmers used to solve things very differently, see the first code-snippet:class Child { public Child() { } private int age = 0; public int GetAge() { return age; } public void SetAge(int _age) { age = _age; } }In the first code-snippet, a programmer in C# 1.0 was required to write two methods to be able to set and get a value, in this context, the age of a child. However, that's a lot of code, and it quickly gets messy and it's not elegant enough. However, this issue was solved in C# 2.0 with the introduction of Properties. The code-snippet below demonstrates Properties.
class Child { public Child() { } private int _age = 0; public int Age { get { return _age; } set { _age = value; } } }The Properties are really useful, especially when we need to protect internal variables and not expose them to the outside world. The second code-snippet above clearly protects our internal variable, _age. However, even the second code-snippet isn't elegant enough, what's elegant is the last code-snippet below. Yes, only get; set; —this is an auto-implemented property (called auto-implemented properties) introduced in C# 3.0.
class Child { public Child() { } public int Age { get; set; } }The last code-snippet is cleaner, safer and more concise since no additional logic is required in the property accessors, in short, more code efficiency benefits.
8. Namespace Alias Qualifier
The Namespace Alias Qualifier (a.k.a. Aliases) in C# lets developers use the alias name instead of the complete namespace. This is very useful, since namespaces in general can become quite long, an alias becomes very handy. In the code-snippet below, Excel is an alias of the Microsoft.Office.Interop.Excel namespace, yeah the default namespace is long, but we've shorten it.using Excel = Microsoft.Office.Interop.Excel; ... var excelapp = new Excel.Application(); excelapp.Visible = true;
9. Object Initializers
The old way, to initialize property values from outside of the class, we would have to write either use a constructor or initialize the properties separately as shown in the first code-snippet:Child child = new Child(); child.Age = 10; child.Name = "Bryan"; child.StreetAddress = "Seattle, WA 98124";The code-snippet above, can be written as:
Child child = new Child() { Age = 10, Name = "Bryan", StreetAddress = "Seattle, WA 98124" };This language feature exists in C# 3.0 and newer versions, but is not supported in C# 1.0 and C# 2.0. In short, this provides a more elegant way to achieve the same thing, in addition, it's a useful shorthand and provides good code productivity.
10. Nullable Types
Every C# developer in the world knows how to work with value types like int, double, bool, char, and so one. They're really useful, but they have one flaw: they simply cannot be set to null, except string which can be set to null. For example, a bool variable can only hold the values true or false, however, putting the question symbol ("?") or as shown below, Nullable it is possible to assign null i.e. undefined.Nullable<bool> status = null; int? i = null;
11. Type Inference
C# 3.0 introduces the concept of type inference with the var keyword. The var keyword was added to support anonymous types which is another new feature in C# 3.0. Without this keyword, we would not be able to create a variable of an anonymous type if we always needed to specify the type. The best part is that the compiler determines the type automatically.string[] Elementaryschools = {"Adams Elementary", "Hyde Elementary School", "Brookland Elementary", "Meyer Elementary School", "Thomas Elementary School", "Young Elementary School"}; var school = from name in Elementaryschools where name[0] == 'T' select name;
foreach (string n in school) Console.WriteLine(n);The output in the console application will be:
Thomas Elementary School
The main reason "Thomas Elementary School" is printed out, is because we say: where name[0] == 'T', we select only the name if it begins with a 'T'. I just wanted to give some code explanation, in case it becomes complicated. The following two declarations of i are functionally equivalent:
int i = 25; //Explicitly typed var i = 25; //Implicitly typed
12. Lambda Expressions
Lambda expressions were introduced in C# 3.0, and they use the lambda operator =>. Lambda expressions provide a more concise, functional syntax for writing anonymous methods (note: anonymous methods were introduced in C# 2.0). Since functional programming requires a more declarative style of writing code, lambda expressions are handy, in short, lambda expressions are simply functions/methods.The following code-snippet shows the definition of a delegate and a method that can be used to initialize the delegate:
public delegate int DoubleInteger(int x); ... class DelegateSample { static public int DoubleNow(int x) { return x * 2; } }Create and initialize an instance of the delegate, and then call it:
DoubleInteger dint = new DoubleInteger(DelegateSample.DoubleNow); Console.WriteLine("{0}", dint(16));Using lambda expressions, the syntax gets even terser:
DoubleInteger dint = x => x * 2; Console.WriteLine("{0}", dint(16));Lambda expressions provide a powerful shorthand that can be used to significantly speed up C# development. In short, lambda expressions allow C# developers to shorten their code significantly and make it more compact form, isn't this good huh? Of course it is good especially if we want to achieve productivity.
13. Optional and Named Parameters in C# 4.0
C# 4.0 introduces optional and named parameters. Named parameters free us from the need to remember or to look up the order of parameters in the parameter lists of invoked methods. Optional parameters enable us to omit arguments for some parameters. These two language features can be used with Microsoft Office Automation APIs—this is what the second code-snippet demonstrates below.Word.Application wordapp = new Word.Application() { Visible = true }; object MissingValue = System.Reflection.Missing.Value; object Filename = @"C:\sampledocument.docx"; object ReadOnly = true; wordapp.Documents.Open(ref Filename, ref MissingValue, ref ReadOnly, ref MissingValue, ref MissingValue, ref MissingValue, ref MissingValue, ref MissingValue,ref MissingValue, ref MissingValue, ref MissingValue, ref MissingValue, ref MissingValue, ref MissingValue, ref MissingValue, ref MissingValue);With the support of optional and named parameters this becomes even shorter:
Word.Application wordapp = new Word.Application() { Visible = true }; wordapp.Documents.Open(@"C:\sampledocument.docx", ReadOnly: true);The Microsoft.Office.Interop.Word has a method, Documents.Open this method has one required parameter, and 15 optional paramters, that's a lot huh? Thanks to named paramters we're able to omit a few or all the optional paramters by using their names and passing the argument value for each—as already demonstrated in the second code-snippet above, ReadOnly: true.
14. Asynchronous Programming with Async and Await in C# 5.0
Finally, as a bonus I've decided to demonstrate the usage of async and await in C# 5.0. Asynchronous and Synchronous programming has always been useful, especially when we want our application to be able to perform multiple operations at the same time i.e. multitasking. However, the traditional way of writing asynchronous applications is usually complicated, resulting in applications that become difficult to—write, debug, and maintain.14.1 The traditional way
public delegate int GetValueAsync(int callDuration); class ProgramA { public ProgramA() { } public int GetValueMethod(int callDuration) { Thread.Sleep(callDuration); int i = ReturnRandomNumber(); return i; } private int ReturnRandomNumber() { return new Random().Next(1, 1000); } }C# 5.0 introduces two new keywords, async and await—these help C# developers build asynchronous applications that are capable of achieving multitasking without performance issues.
14.2 The New Modern way
class ProgramB { public ProgramB() { } public async void PrintAsync() { int a = await GetValueAsync(); Console.WriteLine("The Multiplication of one" + " random number with itself is: {0}", a * a); } private async Task<int> GetValueAsync() { int i = ReturnRandomNumber(); return await Task.FromResult<int>(i); } private int ReturnRandomNumber() { return new Random().Next(1, 1000); } }Create and initialize one instance of each, and then invoke their methods:
static void Main(string[] args) { ProgramA prgm = new ProgramA(); GetValueAsync gasync = new GetValueAsync(prgm.GetValueMethod); IAsyncResult result = gasync.BeginInvoke(2000, null, null); Console.WriteLine("Test method GetValueAsync() from ProgramA begins."); Thread.Sleep(300); int a = gasync.EndInvoke(result); Console.WriteLine("The Multiplication of one random numbers is: {0}", a * a); Console.WriteLine("\n\n"); ProgramB prgmB = new ProgramB(); Console.WriteLine("Test method PrintAsync() from ProgramB begins.\n"); prgmB.PrintAsync(); Console.WriteLine("\n"); Console.ReadKey(); }In conclusion, we can easily avoid performance bottlenecks and enhance the overall responsiveness of our C# applications by using asynchronous programming. The new Visual Studio 2012 and .NET Framework 4.5 leverages asynchronous support via a simplified approach, async programming. In Windows 8 the support for asynchronous programming is much more extended thanks to the Windows Runtime (WinRT).
Comments
Post a Comment