.NET

Not only does .NET enforce runtime type safety, but it is actually possible to create new generic types at runtime! Please take a look at the following program:

  1. using System;
  2. using System.Runtime;
  3. using System.Reflection;
  4. using System.Collections;
  5. namespace console
  6. {
  7. class Program
  8. {
  9. static void printLen(IList list) {
  10. Console.Out.WriteLine(list.Count);
  11. }
  12. static IList newGenericListFor(String className) {
  13. // We can get the Class based on a string value -- a useful feature
  14. // for some meta programming.
  15. var clazz = TypeInfo.GetType(className);
  16. // Build a new list constructor using the class information.
  17. var listCtor = typeof(List<>).MakeGenericType(clazz);
  18. // Construct a new List<clazz> like we did before, only this time
  19. // using reflection at runtime.
  20. //
  21. // Please note the result is cast to a List. This is to ensure
  22. // the standard List methods such as Add, and fields such as Count,
  23. // are accessible via the ints variable.
  24. return (IList) Activator.CreateInstance(listCtor);
  25. }
  26. static void Main(string[] args) {
  27. // Create a List<Int32>.
  28. var ints = newGenericListFor("System.Int32");
  29. // Add some numbers to the list.
  30. ints.Add(1);
  31. ints.Add(2);
  32. ints.Add(3);
  33. if (args.Length > 0 && args[0] == "fail") {
  34. // If this program is executed with a single argument "fail",
  35. // then we try to add "Hello" to the ints list.
  36. //
  37. // This will compile because ints is not known to the compiler
  38. // yet as a List<Int32>, even though that is what it is.
  39. //
  40. // At runtime,however, this will fail because you cannot add a
  41. // String to a List<Int32>.
  42. ints.Add("Hello");
  43. }
  44. // Create a List<String>.
  45. var strs = newGenericListFor("System.String");
  46. // Add some strings to the list.
  47. strs.Add("Hello");
  48. strs.Add("world");
  49. printLen(ints);
  50. printLen(strs);
  51. }
  52. }
  53. }

The code comments in the above program demonstrate that .NET does in fact allow runtime instantiation from generic types, and maintains their runtime type safety, even if this does not exist at compile time due to the meta programming involved.

To run the above program, please follow these instructions:

  1. Launch the container:

    1. docker run -it --rm --cap-add=SYS_PTRACE --security-opt seccomp=unconfined go-generics-the-hard-way

    Please note the --cap-add=SYS_PTRACE --security-opt seccomp=unconfined flags are required in order to use the lldb debugger to attach to a .NET process.

  2. Compile the above program:

    1. dotnet build --debug -p:UseSharedCompilation=false -o ./05-internals/03-runtime-instantiation/dotnet/bin ./05-internals/03-runtime-instantiation/dotnet
  3. Run the program normally:

    1. ./05-internals/03-runtime-instantiation/dotnet/bin/dotnet
    1. 3
    2. 2
  4. Run the program again forcing a runtime type check error:

    1. ./05-internals/03-runtime-instantiation/dotnet/bin/dotnet fail
    1. Unhandled exception. System.ArgumentException: The value "Hello" is not of type "System.Int32" and cannot be used in this generic collection. (Parameter 'value')
    2. at System.Collections.Generic.List`1.System.Collections.IList.Add(Object item)
    3. at console.Program.Main(String[] args) in /go-generics-the-hard-way/05-internals/03-runtime-instantiation/dotnet/main.cs:line 66
    4. Aborted
  5. Type exit to stop and remove the container.

.NET clearly knows what it is doing when it comes to generics and runtime instantiation. Does Go keep up?


Next: Golang