left-icon

C# Succinctly®
by Joe Mayo

Previous
Chapter

of
A
A
A

CHAPTER 9

Moving Forward and More Things to Know

Moving Forward and More Things to Know


To keep subject matter succinct, I’ve passed up features that could evolve into deeper discussions. This chapter is about some of those features, if only to highlight that they are part of the C# language and that you are likely to encounter them regularly.

Decorating Code with Attributes

An attribute is a feature of C# that lets you decorate code with meta-information for various tools. I use the term “tool” loosely, but it could be the C# compiler, a testing framework, or a UI technology. Essentially, these tools read the attributes to make some decision on how to work with your code. I’ll show you a few examples so you can be familiar with attribute syntax when you encounter it in code.

The Obsolete attribute lets you indicate that some code has been deprecated. It’s a C# compiler attribute and the compiler will emit a message regarding its use. The following code shows an example.

using System;

public class ShoppingCart

{

    [Obsolete("Method planned for deprecation on date – use … instead.")]

    public void Add(string item) { }

    [Obsolete("Method is obsolete and can no longer be used", error: true)]

    public decimal CalculateTax(decimal[] prices) { return 0; }

}

When the C# compiler sees the Obsolete attribute decorating Add, it will show a warning with the message argument matching the parameter to the Obsolete attribute. In the second example, the compiler shows the message as an error and you won’t be able to compile because the second parameter, true, indicates that the compiler should treat usage of that method as an error.

The next example uses attributes for a unit test with MSTest, Microsoft’s unit testing software.

using System;

using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace UnitTestProject1

{

    [TestClass]

    public class UnitTest1

    {

        [TestMethod]

        public void TestMethod1()

        {

        }

    }

}

As with most unit testing frameworks, MSTest has a test runner that loads the unit test code, looks for classes decorated with the TestClass attribute, and executes methods with the TestMethod attribute. It does this with another capability of C# called reflection.

Using Reflection

Reflection gives you the ability to examine compiled .NET code. With reflection, you can build useful tools like MSTest, dynamically instantiate types and execute their code, and more. The following code uses reflection to dynamically instantiate a type and execute one of its methods.

using System.Linq;

using System;

using System.Reflection;

public class FinancialCalculator

{

    public decimal Sum(decimal[] numbers)

    {

        return numbers.Sum();

    }

}

public class Program

{

    public static void Main()

    {

        decimal[] prices = { 1m, 2m, 3m };

        Type calcType = typeof(FinancialCalculator);

        MethodInfo sumMethod = calcType.GetMethod("Sum");

        FinancialCalculator calc =

            (FinancialCalculator)Activator.CreateInstance(calcType);

        decimal sum = (decimal)sumMethod.Invoke(calc, new object[] { prices });

        Console.WriteLine($"Sum: {sum}\nPress any key to continue.");

        Console.ReadKey();

    }

}

FinancialCalculator.Sum uses the LINQ Sum method, so add a using clause for System.Linq. Add a using declaration for System.Reflection to support reflection too.

With reflection, a Type instance gives you access to all of the information about a type. The Main method calls GetMethod to obtain a MethodInfo reference to the Add method, but there are many more methods that let you look at various parts of a type. As an example of a subset of capabilities available, you can call GetMethods, GetProperties, or GetFields to get an array of MethodInfo, PropertyInfo, or FieldInfo respectively. There are many more methods in the Type class you can use, and it’s a fun exercise to write code to practice with this.

Activator.CreateInstance creates a new instance of the Type it’s passed. Calling Invoke lets you run a method and get the results. Much of the previous reflection code is hard-coded for simplicity, but it’s very useful for when you need to write code that examines the capabilities of another piece of code and optionally work with the member of a type.

Working with Code Dynamically

C# also has a type called dynamic. Its purpose is to allow you to interoperate with dynamic languages, like IronPython and IronRuby, and makes reflection easier. Microsoft also has a technology called Silverlight. Dynamic could make working with the HTML DOM easier, but Silverlight has been largely replaced by HTML 5 as a dynamic web application technology.

The dynamic type lets you assign any value to a dynamic variable and use any typed members on that variable. Rather than the C# compiler emitting errors, any errors are handled by the CLR at runtime. You might see where this has a lot of power through coding flexibility, yet a drawback of dynamic typing is that it offers no indication of type-related errors until runtime. Using the FinancialCalculator class from the previous example, the following is an example of some dynamic code.

using System;

using System.Reflection;

public class Program

{

    public static void Main()

    {

        decimal[] prices = { 1m, 2m, 3m };

        Type calcType = typeof(FinancialCalculator);

        MethodInfo sumMethod = calcType.GetMethod("Sum");

        dynamic calc = Activator.CreateInstance(calcType);

        dynamic sum = calc.Sum(prices);

        Console.WriteLine($"Sum: {sum}\nPress any key to continue.");

        Console.ReadKey();

    }

}

You might notice that this code is simpler than the full reflection implementation. You don’t have an interface and have no guarantee that the calc instance has a member named Sum, but since the alternative is to use reflection, you’re still in the same situation of runtime evaluation. Therefore, this might be a reasonable approach for this particular scenario.

Pulling together what you learned about generics, it might be useful to improve the algorithm even further, as shown in the following listing.

using System;

public class Program

{

    public static void Main()

    {

        decimal[] prices = { 1m, 2m, 3m };

        decimal sum = GetSum<FinancialCalculator, decimal>(prices);

        Console.WriteLine("Sum: {0}\nPress any key to continue.", sum);

        Console.ReadKey();

    }

    public static TValue GetSum<TCalc, TValue>(TValue[] prices)

        where TCalc : new()

    {

        dynamic calc = new TCalc();

        TValue sum = calc.Sum(prices);

        return sum;

    }

}

The previous example totally eliminates the need for reflection, reduces code, makes the algorithm strongly typed where it needed to be, and makes it dynamic where it helps. It would have been possible to use an interface constraint to make GetSum more strongly typed, but I used this as an exercise to help you think about where dynamic might be useful.

Summary

Attributes are C# features that tell a tool something about your code. Reflection helps you write meta-code that can evaluate and execute other code. There is a dynamic type that lets you make assumptions about the code you’re writing, interfacing with dynamic languages, and making it easy to perform reflection.

This completes C# Succinctly. I hope it has been useful for you. I wish you the best in your further studies.

Scroll To Top
Disclaimer
DISCLAIMER: Web reader is currently in beta. Please report any issues through our support system. PDF and Kindle format files are also available for download.

Previous

Next



You are one step away from downloading ebooks from the Succinctly® series premier collection!
A confirmation has been sent to your email address. Please check and confirm your email subscription to complete the download.