3. Some important things for a X++ developer to know when writing C# code

3.1 Changing types (converting and casting)

C# is an strongly-typed programming language (even if there are different opinions about this, but coding managed, safe code with C# will fit the definition of a strongly-typed programming language). I quote one definition from an article on Wikipedia:

Most generally, "strong typing" implies that the programming language places severe restrictions on the intermixing that is permitted to occur, preventing the compiling or running of source code which uses data in what is considered to be an invalid way. For instance, an integer division operation may not be used upon strings; a procedure which operates upon linked lists may not be used upon numbers. However, the nature and strength of these restrictions is highly variable.

This quote shows that there is no perfectly clear definition for that term, but it explains pretty much what's behind the idea: The compiler has clear rules how to handle type conversions (implicit or explicit) and at runtime the CLR always knows the exact type of each object, so that at any time (compiling and runtime) C# and the CLR is able to verify if a conversion is permitted (and if not to throw an exception in order to prevent a non consistent state of the application).

There are many articles about this subject on the web and for this reason I'd like to explain only the significant differences about the conversion/casting between C# and X++. In the following example I'll have 3 classes as described in the following class diagram:

image_thumb[1]

These classes contain no code. When I now try to convert an object from A01 to B01 and C01 to A01 I would have the following scenario: (Explicitly casting is done in C# as described here on msdn)

    1:      B01 b02 = new B01();
    2:      //Upcasting:
    3:      A01 a02 = (A01) b02;
    4:   
    5:      A01 a01 = new A01();
    6:      //Downcasting:
    7:      //The following line would throw the runtime error:
    8:      //InvalidCastException: Unable to cast object of type 'WinForm.A01' to type 'WinForm.B01'.
    9:      B01 b01 = (B01) a01;
   10:   
   11:      C01 c03 = new C01();
   12:      //"arbitrary" casting since the class A01 has nothing in common with C01
   13:      //The following line would throw the compiler error:
   14:      //Cannot convert type 'WinForm.C01' to 'WinForm.A01'
   15:      //A01 a03 = (A01) c03;

In C# only upcasting (My thoughts about up and downcasting to remember the terms) is by default tolerated. Downcasting results by default in an InvalidCastException. Trying to convert two completely different types results in a compile-time-error as mentioned in line 14 in the example.

All this makes perfect sense. The CLR doesn't accept the downcasting since B01 might have more and different capacities due to the derivation, and so taking a01 and transform it into b01 would be extremely dangerous, so you have to create a new instance of B01 and assign all properties from a01 to this instance. Upcasting in contrast isn't a problem, since the super-class (or in C# the base-class) is a subset of the derived class.

 

3.1.1 Parse-methods and the class Convert.

C# is an object orientated language and follows these principles quite categorically. While you have to convert in the X++ language with the use of build-in function (for example str2int), .Net offers here much more comfort: The methods that converts one type explicitly to another are static methods of the type:

    1:  int.Parse("01");

or you can find other methods for converting in the class System.Convert and will be much easier for you than in X++ (promised ;-).

 

3.1.2 "is" and "as"

In order to cast objects without risking an InvalidCastException, .Net offers you two two keywords that are extremely helpful: is and as.

"is" verifies if a cast can be done during the runtime.

"as" tries to cast and returns null if the cast fails during the runtime, but avoids throwing an InvalidCastException.

I changed the code from the first example by using "is" and "as":

    1:      B01 b02 = new B01();
    2:      //Upcasting:
    3:      A01 a02 = b02 as A01; //since this is permitted
    4:   
    5:      A01 a01 = new A01();
    6:      //Downcasting:
    7:      B01 b01;
    8:      if (a01 is B01) //will be false
    9:          b01 = (B01) a01; //so this will not be executed
   10:      b01 = a01 as B01; //b01 will be null since it would throw an exception
   11:   
   12:      C01 c03 = new C01();
   13:      //"arbitrary" casting since the class A01 has nothing in common with C01 still doesn't work
   14:      //The following compiler exception will be thrown:
   15:      //Cannot convert type 'WinForm.C01' to 'WinForm.A01' via a reference conversion,
   16:      //boxing conversion, unboxing conversion, wrapping conversion, or null type conversion    
   17:      A01 a03 = c03 as A01;

 

3.2 The scope

The scope is an enclosing context where values and expressions are associated and this is in C# different then in X++.  While in X++ the scope for a variable defined in a method is always the entire method, in C# you have local scopes which you can identify with the C-style brackets “{“ and “}”. Here some examples that might give you an idea:

    1:  { 
    2:      string sample1;
    3:      {
    4:           string sample2;
    5:           ... 
    6:      }
    7:      for(int sample3 = 0; sample3  < 10; sample3 ++)
    8:      {
    9:       ...
   10:      }
   11:  }

The scope for sample1 ends at line 11, that for sample2 at line 6 and that for sample3 at 10. By consequence you declare your variable whenever you need it and not always at the beginning. This saves resources, makes your code much easier to read and avoids a lot of not used variables in the beginning of your method. However, once the program left the scope of a variable, the reference to the object, that the variable held is lost and the GC can now take care of the object when this was the only or last reference - at least theoretically. Theoretically because this is true for managed .Net code only, but not for unmanaged code. Since the resources that have been allocated for example by an P/Invoke or COM-objects won’t be released by the GC without your intervention. This means, that the use of unmanaged code is a potential risk of a memory leak when the disposing of the object isn’t well done (chapter 1.4). I wrote about that in the first part (1.4), but Jeffrey Richter’s “CLR via C#” will give you much more information and precision about this subject.

To resume it:

In .Net you do not set variables to null when you want them to be collected by the GC and especially not if you are dealing with unmanaged code!

 

3.3 Releasing and disposing objects

The very simple algorithm of the garbage collector in Dynamics Ax explains why you are releasing and disposing objects in X++ differently than in .Net. One of this differences is assigning null to the variable that isn’t used anymore.

    1:  private void MyMethod()
    2:  MyClass myObject;   
    3:  ;   
    4:  myObject = new MyClass();
    5:   
    6:  ... (your code)   
    7:       
    8:  myObject = null;   
    9:        
   10:  ... (and here it continues)   
   11:  }

In line 8 you are assigning null to the variable myObject, so that the garbage collector (GC) will decrease the number of references to the object that you created in line 4. In the second chapter of this series I already mentioned that as described on Msdn:

X++ objects are destructed automatically when there are no more references to them. You can destruct them explicitly in the following ways:

  • Use the finalize method.
  • Set the object handle to null.

This is not true for the CLR! Line 8 would be bad practice in .Net. The primary reason is the different garbage collector in X++ and the CLR and the “Finalize” method in X++ as described in the first part.

 

3.4 The constructor

In the first chapter I talked about "Finalize", Dispose and why "Finalize" isn't a destructor and I concluded that .Net has no destructor. But it has of course constructors. Precisely it distinguishes between 3 different constructor types: instance constructors for reference types, instance constructors for value types and type constructors (static constructors). A developer’s guide for constructors can be found on msdn. Until now I did not explain the difference between reference types and value types as the subjects have already been sufficiently complex. Many good articles have been published over the years about these subjects (for example here or here), and I apologize for not adding a new one. Since value types (structs) are quite rarely used, I will not treat leave this subject.

 

3.4.1 The instance constructor

So here I'm writing about the  reference type constructors. Any class can have several constructors, but they must be distinguished due their signature. Constructors can of course have different accessibilities. In C# the constructor is defined by the the name of its class like this:

    1:      public class A01
    2:      {
    3:          public A01()
    4:          {}
    5:      }

Here's an example of a very simple class that shows how you can interact with the different constructors of a class with the keyword 'this':

    1:      public class A01
    2:      {
    3:          string _anyString;
    4:          bool isDefaultConstructor = false;
    5:   
    6:          public string AnyString
    7:          {
    8:              get { return _anyString; }
    9:              set { _anyString = value; }
   10:          }
   11:   
   12:          public A01()
   13:              : this("DefaultValue")
   14:          {
   15:              isDefaultConstructor = true;
   16:          }
   17:   
   18:          public A01(string anyString)
   19:          {
   20:              _anyString = anyString;
   21:          }
   22:      }

In line 12 we find the default constructor that automatically calls an overloaded constructor at line 18 and passes him an argument, executes this overloaded constructor (here it's line 20) and then executes the code of the originally called constructor in line 15. It's of interest that line 4 is initialized before the execution of the constructor.

As you can call other constructors within one instance, you can call constructors of derived classes as shown in the following example:

    1:      public class A02 : A01
    2:      {
    3:          string _anyPrivateString;
    4:   
    5:          public A02()
    6:              : base("DefaultValue")
    7:          {
    8:              _anyPrivateString = "AnyValue";
    9:          }
   10:      }

In order to call a constructor of a derived class, you call it with the keyword 'base'. 'base' calls first the constructor of its base-class (line 20 of the first example) and then executes its own code (line 8). Once again it's worth to mention that the field in line 4 is initialized before any constructor is executed.

In his book CLR with C#, Jeffrey Richter pointed out a very important matter that shouldn't be ignored. I quote (Chapter 8, Part III):

You should not call any virtual methods within a constructor that can affect the object being constructed. The reason is if the virtual method is overridden the type being instantiated, the derived type's implementation of the overridden method will execute, but all of the fields in the hierarchy have fully initialized. Calling a virtual method would therefore result in unpredictable behavior.

I find this comment very interesting, since I was faced with a very similar situation in X++ and it took me long time to identify this problem.

 

3.4.2 The type constructor

Type constructors are static constructors and you can find a quite good description on msdn. This type of constructor is rarely used, but due to a particular behavior very interesting: The CLR guarantees the execution of this constructor only once, so that this constructor type is very interesting for the singleton-pattern which I will discuss later (in 3.5).

The type-constructor is executed before the instance constructor and the access modifier can't be modified. It's extremely important that the code in the type-constructor is robust and does not throw exceptions. In that case a System.TypeInitializationException would be thrown and would make the type unusable.

The following code shows the difference execution order of the class A01 with a type-constructor:

    1:      public class A01
    2:      {
    3:          string _anyString;
    4:          bool isDefaultConstructor = false;
    5:   
    6:          public string AnyString
    7:          {
    8:              get { return _anyString; }
    9:              set { _anyString = value; }
   10:          }
   11:   
   12:          static A01()
   13:          {
   14:              //some code
   15:          }
   16:   
   17:          public A01()
   18:              : this("DefaultValue")
   19:          {
   20:              isDefaultConstructor = true;
   21:          }
   22:   
   23:          public A01(string anyString)
   24:          {
   25:              _anyString = anyString;
   26:          }
   27:      }

Line 14 would be executed before line 4!

 

3.5 The properties

Properties are available in most .Net languages and a really great alternative to the getter/setter of X++. The following code shows you the way to use them and demonstrates the interest:

    1:          private string _configValue;
    2:   
    3:          public string ConfigValue
    4:          {
    5:              get
    6:              {
    7:                  return _configValue;
    8:              }
    9:   
   10:              set
   11:              {
   12:                  _configValue = value;
   13:              }
   14:          }

By defining the property “ConfigValue” you will prevent the user from accessing the private declared field “_configValue”. Doing the same thing in X++ would result in the following code:

    1:          private string _configValue;
    2:   
    3:          public string getConfigValue()
    4:          {
    5:              return _configValue;
    6:          }
    7:   
    8:          public void setConfigValue(string value)
    9:          {
   10:              _configValue = value;
   11:          }

Consequently you would have two methods with two different names and signatures. The following code sample shows you a different use:

    1:          public void Sample()
    2:          {
    3:              string myStringValue;
    4:              //using the properties
    5:              myStringValue = ConfigValue;
    6:              ConfigValue = "some Value";
    7:              //using the getter/setter
    8:              myStringValue = getConfigValue();
    9:              setConfigValue("some Value");
   10:          }

What you prefer might be a question of individual taste, but it’s best practice to use properties in C# and using them will make your code more readable for your colleagues.

 







3.6 The modifiers static, readonly and constants

 

3.6.1 Static

Static” is something you are already familiar with in X++. Unfortunately in X++ there are only static methods and no static fields available and this is something that I think is really missing. "Static" means that something belongs to the class and not the instance. In VB.Net this is called “Shared” which I think comes closer to it's intention. There's nothing easier as to define a static variable in C#:

    1:  static string anyString = "Hello World";

In X++ global variables were often used as a workaround and mfp's two cents wrote a good article about the use of global variables.

 

3.6.2 Readonly

C# knows the keyword “readonly” which is called in the IL “initonly” and only assigned in the constructor. This is important for protecting fields during the runtime for any erroneous modification and makes the code much easier to understand as well, since the developer quickly understands that he must not change the value of this field.

    1:  readonly string anyString = "Hello World";

 

3.6.3 Constants

Constants unfortunately don’t exist in X++, neither. In C# you use constants instead of the "famous" macros from Ax. Constants belongs (as static variables) to the class without being static, and their value is compile time read-only, which means, that the value can only be set during the compilation and not at any time later. You might think of some kind of “static readonly”, but without the possibility to assign a value in the constructor.

    1:  const string ANY_STRING = "Hello World (const string)";

So you can use this static variable like this:

    1:  string myString = MyClass.ANY_STRING;

You see, it's so much easier than the macros from Ax, don't you think?

 

3.6.4 Other modifiers

You can find the other modifiers explained here on Msdn.

 

3.5 The singleton-pattern

 The singleton pattern in X++ (which you can find on axaptapedia) is very particular (in contrast to most other languages I know). You can find the default implementation in the Ax-world:

    1:  // RunOn:CalledFrom
    2:  public static SingletonTest instance()
    3:  {
    4:      SingletonTest   singleton;
    5:      SysGlobalCache  globalCache = infolog.objectOnServer() ? appl.globalCache() : infolog.globalCache();
    6:      ;
    7:   
    8:      if (globalCache.isSet(classStr(SingletonTest), 0))
    9:          singleton = globalCache.get(classStr(SingletonTest), 0);
   10:      else
   11:      {
   12:          singleton = new SingletonTest();
   13:          infoLog.globalCache().set(classStr(SingletonTest), 0, singleton);
   14:          appl.globalCache().set(classStr(SingletonTest), 0, singleton);
   15:      }
   16:   
   17:      return singleton;
   18:  }
  

In C# there are many different variation. My preferred one is the following:

    1:      internal class S01
    2:      {
    3:          //the static variable that holds the single instance should be
    4:          //readonly, since it's instance should be set just once and this during 
    5:          //the initialization of the type...
    6:          private static readonly S01 _instance;
    7:   
    8:          //any dummy-value to access... 
    9:          private string _anyValue = "Hello World";
   10:   
   11:          /// since it's readonly we only need a get for the property     
   12:          public static S01 Instance
   13:          {
   14:              get { return _instance; }
   15:          }
   16:   
   17:          public string AnyValue
   18:          {
   19:              get { return _anyValue; }
   20:              set { _anyValue = value; }
   21:          }
   22:   
   23:     
   24:          ///By making the default constructor private we'll get sure that
   25:          ///an instance can only be created by it's type.
   26:          private S01() {
   27:              //any other code 
   28:          }
   29:   
   30:          /// As only one thread in an AppDomain (A .Net process) can execute the type
   31:          /// constructor at the same time, this is the place to instantiate the single
   32:          /// instance of S01
   33:          static S01()
   34:          {
   35:              _instance = new S01();
   36:          }
   37:      }
  

The code stays simple and it's "threadsafe". (Please don't pay too much attention to the AnyValue-property in this class, since it's just a dummy property to show you in the second part the use of this singleton-class:

    1:  string str = S01.Instance.AnyValue;
  

The first thing executed in the S01-lass is the type-constructor. He would be called even if the field "_instance" would be initialized. As I already explained before, type-constructors are absolutely thread-safe since only can be executed once in one AppDomain and there is no concurrence problem when called from different threads.

 

3.6 Strings

The  System.String class is a very particular class. It's a immutable and this means that the value can't be changed once the object has been created. In order to be able to nevertheless change string-values (The language C# would be unusable otherwise), a new object is created to replace the old one. If you would like to concatenate the string objects str1 and str2 ( str1 = str1 + str2; ) a third string object with the value of str1 + str2 is created and assigned to str1. The creation of String-objects does not perform well and this is the reason that you should use the  System.Text.StringBuilder class for string-constructions like concatenations which is much faster and optimized for those kind of operations.

    1:  StringBuilder stringBuilder = new StringBuilder("The first text");
    2:  stringBuilder.Append("This is the next text I want to append to the first one");
    3:  string myText = stringBuilder.ToString();

Since this behavior is completely different in X++ it's important for you to notice this difference.

 

3.8 Instrumentation

Jon Fencey wrote already 2004 a great article about code instrumentation with .Net and it is still a great source of inspiration even though the technologies have progressed since then. As he wrote in his article:

Instrumentation allows you to determine the running state of a system once it has been deployed

And that's probably why code instrumentation is one of the most ignored parts in the architecture of applications: It becomes important after the deployment. It's no funky option with which you might impress the person in charge for the budget of your project, but it will help you to save a lot of money when it is well implemented. Since there are already great articles on all these points of code instrumentation I'm restricting this part only to the most important information (at least most important for me)

 

3.8.1 Code tracing

How to add tracing information is rather well in this msdn article. The methods that helps you tracing your information can be found in the System.Diagnostics.Trace class. Writing traces is quite easy and the following code-example shows this:

    1:              //TODO: please find a really meaningful information to trace !
    2:              //This instruction will write an entire line in the EventLog
    3:              System.Diagnostics.Trace.WriteLine("Something happened in the application.");
    4:              bool isTrace = true;
    5:              System.Diagnostics.Trace.WriteLineIf(isTrace,
    6:                 "Something useful to trace.");
    7:              int loops = 4;
    8:              System.Diagnostics.Trace.Assert(loops == 5, "loops is equal to 5.");

These traces are caught by trace listeners that decides where to write these traces and that can be configured in your App.config with the trace Xml-element like this: (and you can add as many as you need in order to write to multiple trace destinations).

    1:      <system.diagnostics>
    2:        <trace autoflush="true" indentsize="4">
    3:          <listeners>
    4:            <add name="myListener"
    5:              type="System.Diagnostics.TextWriterTraceListener"
    6:              initializeData="c:\\temp\\TextWriterOutput.log" />
    7:            <add name="myListener2"
    8:              type="System.Diagnostics.EventLogTraceListener"
    9:              initializeData="Application" /> <!--since Application is already existing in your Windows Event-log-->
   10:          <!--remove name="Default" /--> <!--This removes the default event trace listener when uncommented -->
   11:          </listeners>
   12:        </trace>
   13:      </system.diagnostics>

Or you can explicitly define the trace listener in your code like this:

    1:              System.Diagnostics.Trace.Listeners.Add(
    2:                 new System.Diagnostics.TextWriterTraceListener("c:\\temp\\test.log"));.csharpcode {
   background-color: #ffffff; font-family: consolas, "Courier New", courier, monospace; color: black; font-size: small
}
.csharpcode pre {
   background-color: #ffffff; font-family: consolas, "Courier New", courier, monospace; color: black; font-size: small
}
.csharpcode pre {
   margin: 0em
}
.csharpcode .rem {
  color: #008000
}
.csharpcode .kwrd {
  color: #0000ff
}
.csharpcode .str {
   color: #006080
}
.csharpcode .op {
    color: #0000c0
}
.csharpcode .preproc {
   color: #cc6633
}
.csharpcode .asp {
   background-color: #ffff00
}
.csharpcode .html {
   color: #800000
}
.csharpcode .attr {
  color: #ff0000
}
.csharpcode .alt {
   background-color: #f4f4f4; margin: 0em; width: 100%
}
.csharpcode .lnum {
 color: #606060
}

As you might have noticed there are different types of trace listeners derived from System.Diagnostics.TraceListener. The following are those already available by the .Net framework.

You are of course free to create your own ones. Mansoor Ahmed Siddiqui explained all this in much more details in his article on 15 seconds. You should have a look at this article since he explains all essential information briefly and comprehensively.

 

3.8.2 Debugging

Some rather nice features of C# is the conditional compilation, which enables you compile your code depending on the configuration (for example DEBUG or RELEASE). It's a possible solution in order to identify strange behaviors or exhaustive traces A good explanation is available on msdn.

 

3.8.3 Performance counters

The best explanation of performance counters is delivered by msdn:

Counters are used to provide information as to how well the operating system or an application, service, or driver is performing. The counter data can help determine system bottlenecks and fine-tune system and application performance. The operating system, network, and devices provide counter data that an application can consume to provide users with a graphical view of how well the system is performing.

Using performance counter and creating application specific performance counter can make the behavior of your application in production transparent and make it possible to react proactive to certain problems.

A nice introduction about creating and using performance counter with C# has been written by Michael Groeger and is available on The Code Project here.

 

3.8.4 Event logs

 Event-logs are quite similar to traces, but they should only inform you about what the application did at a certain time: Informing about an event. They aren't intended to verbose information like exception stacks etc. and you only can write into the Windows EventLog which you can visualize by the EventViewer.

You find a good explanation about how to use them in C# on BlackWasp and on msdn.