Vectors for the C# Developer

If you've come to C# from a language such as C++, you might very much miss one of its most powerful features, the 'Vectors' namespace.

C++ developers new to C# often ask how they can use vectors in a C# application, and often one of the first questions (if stack overflow is any evidence) is "Where is the .NET Vector class"?

.NET, as such, doesn't actually have a "Vector" class (well, at least not in the same manner that C++ has). There are classes for vector arithmetic, and even handling matrices, but there's no actual "std::vector" as you would recognize it.

That's not to say, however, that the facilities provided by it are not available elsewhere in .NET.

So, What Does .NET Have Instead?

.NET has something called generics.

Generics allow you to provide the type of data or user defined object at run time. Generics comes in very handy for things like database access libraries, whereby you can have a single implementation of code that can accept multiple different types of object classes.

Generics, however, also tie in very, very nicely with .NET collections in the "System.Collections" namespace.

Let's look at a simple example. Fire up Visual Studio, and create a console mode application.

Right-click your project, add a new item, and create an interface:

Vector1
Figure 1: Creating a new interface

Let's call this interface "IGoodObjects". Once the interface is created, make sure it has the following code in it:

namespace generics_vectors
{
   public interface IGoodObjects
   {
      string PropertyOne { get; set; }
      int PropertyTwo { get; set; }
      bool PropertyThree { get; set; }
   }
}

If you've never used interfaces before, they are at the very core of Object Orientated Programming in .NET.

An interface defines a contract that any object implementing the interface MUST obey.

In the preceding example, any object or class that uses "IGoodObjects" will have to implement "PropertyOne", "PropertyTwo", and "PropertyThree" properties, and, what's more, will have to implement them with exactly the same data types too.

What it means, however, is that you can give a common base type to different objects.

Add two new classes to your console project, "GoodObjectOne" and "GoodObjectTwo", and make sure they contain the following code:

GoodObjectOne.cs

namespace generics_vectors
{
   public class GoodObjectOne : IGoodObjects
   {
      public string PropertyOne { get; set; }
      public int PropertyTwo { get; set; }
      public bool PropertyThree { get; set; }
      public int ThisIsAPropertyThatOnlyGoodObjectOneHas
         { get; set; }

   }
}

GoodObjectTwo.cs

using System;

namespace generics_vectors
{
   public class GoodObjectTwo : IGoodObjects
   {
      public string PropertyOne { get; set; }
      public int PropertyTwo { get; set; }
      public bool PropertyThree { get; set; }
      public int ThisIsAPropertyThatOnlyGoodObjectTwoHas
         { get; set; }
   }
}

You'll see immediately that both classes are almost identical—except for the fact that they both have a custom property of their own.

For now, I'm just going to ignore the specific properties (they were added just to show that the object definition can vary).

For now, go back to your "Program.cs" file, and add the following code:

namespace generics_vectors
{
   class Program
   {
      static void Main(string[] args)
      {
         GoodObjectOne objectOne = new GoodObjectTwo();
         GoodObjectTwo objectTwo = new GoodObjectOne();

      }
   }
}

If your Visual Studio is working correctly, you should see that both lines have errors:

Vector2
Figure 2: The Squiggly red lines indicate errors

This is because even though the two objects have the same call structure, they are not the same type of object. If, however, we change our object definitions to use the interface, we should now see that the errors go away:

namespace generics_vectors
{
   class Program
   {
      static void Main(string[] args)
      {
         IGoodObjects objectOne = new GoodObjectTwo();
         IGoodObjects objectTwo = new GoodObjectOne();

      }
   }
}

I'll not do another screen shot, but you should observe that Visual Studio no longer complains about the type errors you had just a moment ago.

This is because, with the use of the interface, C# has now been told that it can be sure those objects have those three properties, and indeed if you try to write code to access them, you'll find everything works as expected.

Okay, I Get That. Lots of Languages Do This, but That's not Generics

You're absolutely correct that it's not generics, but it's an important concept that helps us further understand how this relates to the C++ vector type.

Generics can support different object types as shown above, without using interfaces to bind to a known set of functionality.

Add a new class to your console app. Let's call this class "GoodObjectWorker". Make sure it has the following code in it:

using System;

namespace generics_vectors
{
   public class GoodObjectWorker<T> where T : IGoodObjects
   {
      private T _inboundObject;

      public GoodObjectWorker(T initialObject)
      {
         _inboundObject = initialObject;
      }

      public void SetUpData()
      {
         _inboundObject.PropertyOne = "This is a string";
         _inboundObject.PropertyTwo = 20;
         _inboundObject.PropertyThree = false;

      }

      public void DisplayData()
      {
         Console.WriteLine("Property One: {0}",
            _inboundObject.PropertyOne);
         Console.WriteLine("Property Two: {0}",
            _inboundObject.PropertyTwo);
         Console.WriteLine("Property Three: {0}",
            _inboundObject.PropertyThree);

         Console.WriteLine(typeof(T));

       }

   }
}

This class pretty much looks the same as a regular .NET class, except for the strange "<T>" part just after the object name and the where statement following it.

This expanded class name basically says that, when I use the class "GoodObjectWorker", I'm expecting to also be passed a type of object, unknown at creation time, but that obeys the "IGoodObjects" contract interface.

To be clear here, you DON'T have to provide an interface for an object. The whole

where T : IGoodObjects

can be removed if you want, but using an interface means that you and anyone else using your data object can be sure a given set of functionality is present.

You'll also notice that now, in the methods in this class, instead of supplying the interface type, or indeed any type, all we simply provide is 'T'. The .NET runtime will change this to whatever type you pass to the object at runtime, and then use that object as instructed in other code.

If you now change "Program.cs" to:

namespace generics_vectors
{
   class Program
   {
      static void Main(string[] args)
      {
         GoodObjectWorker<GoodObjectOne> workerOne = new
            GoodObjectWorker<GoodObjectOne>(new GoodObjectOne());
         GoodObjectWorker<GoodObjectTwo> workerTwo = new
            GoodObjectWorker<GoodObjectTwo>(new GoodObjectTwo());

         workerOne.SetUpData();
         workerTwo.SetUpData();

         workerOne.DisplayData();
         workerTwo.DisplayData();

      }
   }
}

and run your program, you should see the following:

Vector3
Figure 3: The type of object is identified

You can see immediately that it's aware of the type of object. Both instances are functionally identical.

This now brings us to the final part of this post, and the reason for the title.

Generic Lists (and, for that matter, most of the collections in the "System.Collections" namespace) are already generics aware.

A C++ programmer really just needs to understand that "IList<T>", "ArrayList<T>", "Stack", "Queue", and many others are already there and ready to go.

For example:

std::Vector<int>

is equal to

var List<int>

in C#. However, because of generics, you can easily use:

var List<myObject>

to provide an enumerable list that CAN also be accessed by array index, in exactly the same way you would use

std::Vector<myObject>

The major difference in the .NET space is mainly that there are far more specialized implementations (Such as Stack, Queue, and so on) that implement already known common data access patterns. With C++, you often need to build this functionality yourself. Secondly, the methods known to C++ developers are not an exact match, so it may take you an afternoon of reading the MSDN page describing "System.collections":

https://msdn.microsoft.com/en-us/library/system.collections(v=vs.110).aspx

Want to shout at me with comments, questions, or anything else? As long as it's .NET related, feel free to come find me on Twitter as @shawty_ds. You never know; you might see your question answered in this column.



Related Articles

Comments

  • Good One

    Posted by Vikram on 08/07/2015 02:47am

    Interfaces and generics well explained in one post

    Reply
Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

Most Popular Programming Stories

More for Developers

RSS Feeds

Thanks for your registration, follow us on our social networks to keep up-to-date