Related
What makes .Net IEnumerable so special that compiler types like arrays can be passed as an argument in its place with it being a class library interface. Is there some sort of inheritance in the background?
What actually happens when an array can interchangeably replace a collection or IEnumerable as a parameter:
public void DoJob(IEnumerable<Job> jobs)
{
}
Like this method call:
Job job = new Job();
DoJob(new [] { job });
Since you came from Java, I will start by telling you that in C#, there is a unified type system, where every type derives from object, unlike in Java where there are special "primitives".
So in C#, arrays are just another type. Who says they can't implement interfaces? They can! All the compiler provides is some syntax for creating them. In actuality, arrays' type is System.Array.
This is its declaration:
public abstract class Array : ICloneable, IList, ICollection,
IEnumerable, IStructuralComparable, IStructuralEquatable
See IEnumerable in there?
EDIT:
For IEnumerable<T>, we can find it in the language spec:
Section 12.1.2:
A one-dimensional array T[] implements the interface
System.Collections.Generic.IList (IList for short) and its base
interfaces.
IList<T> implements IEnumerable<T>.
Arrays are a special case in .NET, because you are not allowed to directly inherit from the Array class.
From the documentation:
Single-dimensional arrays implement the IList<T>, ICollection<T>, IEnumerable<T>, IReadOnlyList<T> and IReadOnlyCollection<T> generic interfaces. The implementations are provided to arrays at run time, and as a result, the generic interfaces do not appear in the declaration syntax for the Array class.
This is due to the fact that generics did not exist in .NET 1.0, and to prevent breaking changes, a compiler hack had to be applied.
IEnumerable is an Interface and what you're referring to is Polymorphism.
Arrays, Lists, Collections, and many other types in .NET have a base IEnumerable interface at a minimum. If you make the parameter request Job[] then only an array of Job could be used, meaning a List or other collection would have to be converted to an Array before it could be passed to the parameter. This means the method, hopefully is dealing with and expecting the array for a specific reason. If the method doesn't care about the type of collection, list, or array and simply wants to iterate through the items, then it should look lower in the inheritance tree so that many types can use the method. Making the method IEnumerable<Job> allows the method to work with ANY type that derives from IEnumerable<T> where, in this case, T is of type Job.
Knowing this a List<Job>, ObservableCollection<Job>, Job[] or linq query with select of Job plus many more can use this method.
AFAIK at the infrastructure (CLI/.Net/Mono) level there is a single type to represent arrays: System.Array.
Physically this is a linear sequence of values, but logically they can be considered are organized in more than one dimension.
At the language level (e.g. C#) this logical view benefits from some syntactic sugar:
2D: T[,]
3D: T[,,]
42D: T[,,,...,,,]
There is an obvious parametric polymorphism as behind a 1D array for example can hide more than one type of array:
Array of integers: int[]
Array of strings references: string[]
Array of objects references: object[]
But how would you describe the structural polymorphism, the fact that the array can have multiple dimensions?
At the infrastructure level this is no more a part of the type system, and is only a logical view, so I think there is no polymorphism at all.
But at the language level it might be considered as some kind of inclusion polymorphism as all the arrays are logically presented as different types inheriting from a common base class.
Any input and correction is welcome.
The System.Array class has an essentially-infinite family of derivatives, one for each element type and possible number of dimensions. Because arrays predate generics, they have a special "quasi-hardcoded" means of accomplishing this. Basically, the .NET Framework contains a special hard-coded "recipe" so that given a base-item type and a number of dimensions, it can generate a type, derived from System.Array, which will act as an array with that specified base-item type and dimensionality. Further, because of the way array covariance works, arrays can behave as though they implement interfaces which are not actually part of their class, in ways that no other type can. For example, because a reference of type Cat[] is also a reference of type Animal[], Cat[] implements not only IList<Cat> but also IList<Animal>.
There is no mechanism via which any type other than System.Array can support such covariance, nor is there any way via which user code can define a family of classes such that for any integer n there will be a derivative whose get method takes n parameters. The generic types in .NET can do many things which arrays cannot, but since arrays are implemented via a mechanism other than the generic type facility, they can support some features that facility cannot.
All derived array types descend from System.Array and override the appropriate methods. Arrays are a bit special, though, in the sense that there is baked-in support in the CLR in the form of IL opcodes that interact directly with concrete array types. Still, though, this is the same kind of base class polymorphism as with other classes, at least when accessed via the Array class.
However, arrays are not actually 'polymorphic' in dimensionality in the sense you're thinking. A particular array instance is assigned a particular dimensionality when it's constructed and it can only be used with that dimensionality. This occurs both at the op code level and the GetValue methods. The IList implementation for 'Item' (the indexer) also only supports a single dimension; if you attempt to use it on a multi-dimensional array, you'll get an ArgumentException.
Also, just in case it's not clear, the A[x,y] syntax is actually not syntactic sugar. Multi-dimensional arrays are actually excluded from the built-in CLR support and instead are accessed via methods. This compiles to something like the following IL:
Ldloc A //(load array A onto the operation stack)
Ldloc x //(load value of variable x onto the operation stack)
Ldloc y //(load value of variable y onto the operation stack)
call int32[,]::Get(int, int) //(return a 4-byte signed integer from the array at indices [x,y], putting the result onto the operation stack)
You can compare this to the same for a single-dimensional array A[x]:
Ldloc A //(load array A onto the operation stack)
Ldloc x //(load value of variable x onto the operation stack)
Ldelem_I4 //(return the 4-byte signed integer from the array at index [x] and put the result onto the operation stack)
But how would you describe the structural polymorphism, the fact that the array can have multiple dimensions?
Three points:
At the language level, multidimensional arrays would qualify as internal polymorphism
Since multidimensional arrays can be emulated with associative arrays or lists, it need not depend on a specific data structure
Since multidimensional arrays can be created at runtime, it need not depend on type checking
References
Create a two-dimensional array at runtime
OO Uber-Zealotry Considered Silly
how c++ implements the polymorphism internally?
Refactoring a Switch Statement
C2 Wiki: External Polymorphism
I just don't get something in the .NET generic type casting.
Can someone explain what happens in the following code snippet?
void Main()
{
IEnumerable<int> ints = new List<int>();
IEnumerable<string> strings = new List<string>();
var rez1=(IEnumerable<object>)ints; //runtime error
var rez2=(IEnumerable<object>)strings; //works
var rez3=(List<object>)strings; //runtime error
}
Let's start with the second line which is easiest.
That cast works because the type parameter of IEnumerable<T> is now covariant (that's what the out in out T does). This means you can cast an IEnumerable<Derived> to an IEnumerable<Base> freely.
The first line, which would seem to be the same case, does not work because int is a value type. Interface variance does not work with value types at all because value types do not really inherit from System.Object; they can be boxed into an object, but that's not the same. The documentation mentions that
Variance applies only to reference types; if you specify a value type
for a variant type parameter, that type parameter is invariant for the
resulting constructed type.
Finally, the third line does not work because the type parameter of List<T> is invariant. You can see there is no out on its type parameter; the rules disallow that because List<T> is not an interface:
In the .NET Framework 4, variant type parameters are restricted to
generic interface and generic delegate types.
This is because interface covariance only works with reference types. Int32, of course, is a value type.
This gives more information: http://blogs.msdn.com/b/ericlippert/archive/2009/11/30/what-s-the-difference-between-covariance-and-assignment-compatibility.aspx
And so does this: http://ericlippert.com/2011/09/19/inheritance-and-representation/
The definition of every type which derives from System.ValueType, with the exception of System.Enum, actually defines two kinds of things: a heap object type, and a storage-location type. Instances of the latter may be implicitly converted to the former (making a copy of the data contained therein), and instances of the former may be explicitly typecast to the latter (likewise); even though both kinds of things are described by the same System.Type, and although they have the same members, they behave very differently.
A List<AnyClassType> will expect to hold a bunch of heap-object references; whether the list in question is a List<String>, List<StringBuilder>, List<Button>, or whatever, may be of interest to users of the list, but isn't really of interest to the List<T> itself. If one casts a List<Button> to an IEnumerable<Control>, someone who calls its GetEnumerator() method will expect to get an object which will output references to heap objects that derive from Control; the return from List<Button>.GetEnumerator() will satisfy that expectation. By contrast, if someone were to cast a List<Int32> to List<Object>, someone who called GetEnumerator() would expect something that would output heap object references, but List<Integer>.GetEnumerator will instead yield something that outputs value-type integers.
It's possible to store Int32 values into a List<Object> or a List<ValueType>; storing an integer to such a list will convert it to its heap object form and store a reference to it; calling GetEnumerator() would yield something that outputs heap references. There is no way to specify, however, that such a list will only contain instances of the heap type corresponding to Int32. In C++/CLI, it's possible to declare variables of type "reference to heap-stored valuetype", but the mechanisms behind generic types in .net cannot work with such types.
So as you may know, arrays in C# implement IList<T>, among other interfaces. Somehow though, they do this without publicly implementing the Count property of IList<T>! Arrays have only a Length property.
Is this a blatant example of C#/.NET breaking its own rules about the interface implementation or am I missing something?
So as you may know, arrays in C# implement IList<T>, among other interfaces
Well, yes, erm no, not really. This is the declaration for the Array class in the .NET 4 framework:
[Serializable, ComVisible(true)]
public abstract class Array : ICloneable, IList, ICollection, IEnumerable,
IStructuralComparable, IStructuralEquatable
{
// etc..
}
It implements System.Collections.IList, not System.Collections.Generic.IList<>. It can't, Array is not generic. Same goes for the generic IEnumerable<> and ICollection<> interfaces.
But the CLR creates concrete array types on the fly, so it could technically create one that implements these interfaces. This is however not the case. Try this code for example:
using System;
using System.Collections.Generic;
class Program {
static void Main(string[] args) {
var goodmap = typeof(Derived).GetInterfaceMap(typeof(IEnumerable<int>));
var badmap = typeof(int[]).GetInterfaceMap(typeof(IEnumerable<int>)); // Kaboom
}
}
abstract class Base { }
class Derived : Base, IEnumerable<int> {
public IEnumerator<int> GetEnumerator() { return null; }
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return GetEnumerator(); }
}
The GetInterfaceMap() call fails for a concrete array type with "Interface not found". Yet a cast to IEnumerable<> works without a problem.
This is quacks-like-a-duck typing. It is the same kind of typing that creates the illusion that every value type derives from ValueType which derives from Object. Both the compiler and the CLR have special knowledge of array types, just as they do of value types. The compiler sees your attempt at casting to IList<> and says "okay, I know how to do that!". And emits the castclass IL instruction. The CLR has no trouble with it, it knows how to provide an implementation of IList<> that works on the underlying array object. It has built-in knowledge of the otherwise hidden System.SZArrayHelper class, a wrapper that actually implements these interfaces.
Which it doesn't do explicitly like everybody claims, the Count property you asked about looks like this:
internal int get_Count<T>() {
//! Warning: "this" is an array, not an SZArrayHelper. See comments above
//! or you may introduce a security hole!
T[] _this = JitHelpers.UnsafeCast<T[]>(this);
return _this.Length;
}
Yes, you can certainly call that comment "breaking the rules" :) It is otherwise darned handy. And extremely well hidden, you can check this out in SSCLI20, the shared source distribution for the CLR. Search for "IList" to see where the type substitution takes place. The best place to see it in action is clr/src/vm/array.cpp, GetActualImplementationForArrayGenericIListMethod() method.
This kind of substitution in the CLR is pretty mild compared to what happens in the language projection in the CLR that allows writing managed code for WinRT (aka Metro). Just about any core .NET type gets substituted there. IList<> maps to IVector<> for example, an entirely unmanaged type. Itself a substitution, COM doesn't support generic types.
Well, that was a look at what happens behind the curtain. It can be very uncomfortable, strange and unfamiliar seas with dragons living at the end of the map. It can be very useful to make the Earth flat and model a different image of what's really going on in managed code. Mapping it to everybody favorite answer is comfortable that way. Which doesn't work so well for value types (don't mutate a struct!) but this one is very well hidden. The GetInterfaceMap() method failure is the only leak in the abstraction that I can think of.
New answer in the light of Hans's answer
Thanks to the answer given by Hans, we can see the implementation is somewhat more complicated than we might think. Both the compiler and the CLR try very hard to give the impression that an array type implements IList<T> - but array variance makes this trickier. Contrary to the answer from Hans, the array types (single-dimensional, zero-based anyway) do implement the generic collections directly, because the type of any specific array isn't System.Array - that's just the base type of the array. If you ask an array type what interfaces it supports, it includes the generic types:
foreach (var type in typeof(int[]).GetInterfaces())
{
Console.WriteLine(type);
}
Output:
System.ICloneable
System.Collections.IList
System.Collections.ICollection
System.Collections.IEnumerable
System.Collections.IStructuralComparable
System.Collections.IStructuralEquatable
System.Collections.Generic.IList`1[System.Int32]
System.Collections.Generic.ICollection`1[System.Int32]
System.Collections.Generic.IEnumerable`1[System.Int32]
For single-dimensional, zero-based arrays, as far as the language is concerned, the array really does implement IList<T> too. Section 12.1.2 of the C# specification says so. So whatever the underlying implementation does, the language has to behave as if the type of T[] implements IList<T> as with any other interface. From this perspective, the interface is implemented with some of the members being explicitly implemented (such as Count). That's the best explanation at the language level for what's going on.
Note that this only holds for single-dimensional arrays (and zero-based arrays, not that C# as a language says anything about non-zero-based arrays). T[,] doesn't implement IList<T>.
From a CLR perspective, something funkier is going on. You can't get the interface mapping for the generic interface types. For example:
typeof(int[]).GetInterfaceMap(typeof(ICollection<int>))
Gives an exception of:
Unhandled Exception: System.ArgumentException: Interface maps for generic
interfaces on arrays cannot be retrived.
So why the weirdness? Well, I believe it's really due to array covariance, which is a wart in the type system, IMO. Even though IList<T> is not covariant (and can't be safely), array covariance allows this to work:
string[] strings = { "a", "b", "c" };
IList<object> objects = strings;
... which makes it look like typeof(string[]) implements IList<object>, when it doesn't really.
The CLI spec (ECMA-335) partition 1, section 8.7.1, has this:
A signature type T is compatible-with a signature type U if and only if at least one of the following holds
...
T is a zero-based rank-1 array V[], and U is IList<W>, and V is array-element-compatible-with W.
(It doesn't actually mention ICollection<W> or IEnumerable<W> which I believe is a bug in the spec.)
For non-variance, the CLI spec goes along with the language spec directly. From section 8.9.1 of partition 1:
Additionally, a created vector with element type T, implements the interface System.Collections.Generic.IList<U>, where U := T. (§8.7)
(A vector is a single-dimensional array with a zero base.)
Now in terms of the implementation details, clearly the CLR is doing some funky mapping to keep the assignment compatibility here: when a string[] is asked for the implementation of ICollection<object>.Count, it can't handle that in quite the normal way. Does this count as explicit interface implementation? I think it's reasonable to treat it that way, as unless you ask for the interface mapping directly, it always behaves that way from a language perspective.
What about ICollection.Count?
So far I've talked about the generic interfaces, but then there's the non-generic ICollection with its Count property. This time we can get the interface mapping, and in fact the interface is implemented directly by System.Array. The documentation for the ICollection.Count property implementation in Array states that it's implemented with explicit interface implementation.
If anyone can think of a way in which this kind of explicit interface implementation is different from "normal" explicit interface implementation, I'd be happy to look into it further.
Old answer around explicit interface implementation
Despite the above, which is more complicated because of the knowledge of arrays, you can still do something with the same visible effects through explicit interface implementation.
Here's a simple standalone example:
public interface IFoo
{
void M1();
void M2();
}
public class Foo : IFoo
{
// Explicit interface implementation
void IFoo.M1() {}
// Implicit interface implementation
public void M2() {}
}
class Test
{
static void Main()
{
Foo foo = new Foo();
foo.M1(); // Compile-time failure
foo.M2(); // Fine
IFoo ifoo = foo;
ifoo.M1(); // Fine
ifoo.M2(); // Fine
}
}
IList<T>.Count is implemented explicitly:
int[] intArray = new int[10];
IList<int> intArrayAsList = (IList<int>)intArray;
Debug.Assert(intArrayAsList.Count == 10);
This is done so that when you have a simple array variable, you don't have both Count and Length directly available.
In general, explicit interface implementation is used when you want to ensure that a type can be used in a particular way, without forcing all consumers of the type to think about it that way.
Edit: Whoops, bad recall there. ICollection.Count is implemented explicitly. The generic IList<T> is handled as Hans descibes below.
Explicit interface implementation. In short, you declare it like void IControl.Paint() { } or int IList<T>.Count { get { return 0; } }.
It's no different than an explicit interface implementation of IList. Just because you implement the interface doesn't mean its members need to appear as class members. It does implement the Count property, it just doesn't expose it on X[].
With reference-sources being available:
//----------------------------------------------------------------------------------------
// ! READ THIS BEFORE YOU WORK ON THIS CLASS.
//
// The methods on this class must be written VERY carefully to avoid introducing security holes.
// That's because they are invoked with special "this"! The "this" object
// for all of these methods are not SZArrayHelper objects. Rather, they are of type U[]
// where U[] is castable to T[]. No actual SZArrayHelper object is ever instantiated. Thus, you will
// see a lot of expressions that cast "this" "T[]".
//
// This class is needed to allow an SZ array of type T[] to expose IList<T>,
// IList<T.BaseType>, etc., etc. all the way up to IList<Object>. When the following call is
// made:
//
// ((IList<T>) (new U[n])).SomeIListMethod()
//
// the interface stub dispatcher treats this as a special case, loads up SZArrayHelper,
// finds the corresponding generic method (matched simply by method name), instantiates
// it for type <T> and executes it.
//
// The "T" will reflect the interface used to invoke the method. The actual runtime "this" will be
// array that is castable to "T[]" (i.e. for primitivs and valuetypes, it will be exactly
// "T[]" - for orefs, it may be a "U[]" where U derives from T.)
//----------------------------------------------------------------------------------------
sealed class SZArrayHelper {
// It is never legal to instantiate this class.
private SZArrayHelper() {
Contract.Assert(false, "Hey! How'd I get here?");
}
/* ... snip ... */
}
Specifically this part:
the interface stub dispatcher treats this as a special case, loads up
SZArrayHelper, finds the corresponding generic method (matched simply
by method name), instantiates it for type and executes it.
(Emphasis mine)
Source (scroll up).
int[] a = new int[5];
string[] b = new string[1];
The types of both a and b inherit from the abstract System.Array, but there is no real classes in the built-in library(it seems that there are some runtime types, you can't find the type defination class of an int[]). Can you tell me what happens while compiling? And why did they(the c# team) make this design(I mean why it's not something like Array<T>,instead they are using an abstract class with compiler magics)?
Trying to reason this out within the .NET type system doesn't get you very far. There is core support built into the JIT compiler and the CLR to deal with creating arrays. A statement like this:
var arr = new int[5];
Generates this IL:
IL_0001: ldc.i4.5
IL_0002: newarr [mscorlib]System.Int32
Which the JIT compiler then translate into this machine code:
00000035 mov edx,5 ; arg2 = array size
0000003a mov ecx,6F535F06h ; arg1 = typeof(int)
0000003f call FFD52128 ; call JIT_NewArr1(type, size)
Core ingredients here are the dedicated IL opcode, newarr, instead of the usual newobj opcode that creates an instance of a class. And the simple translation to a CLR helper function that actually gets the object created. You can have a look-see at this helper function with the SSCLI20 source code, clr\src\vm\jithelpers.cpp. Too large to post here, but it is heavily optimized to make this kind of code run as fast possible, having direct access to the type internals available to CLR code.
There are two of these helpers available, JIT_NewArr1() creates one-dimensional (vector) arrays and JIT_NewMDArr() creates multi-dimensional arrays. Compare to the two overloads available for Type.MakeArrayType().
And why did they(the c# team) make
this design(I mean why it's not
something like Array...
Generics are ideal for defining a container, as they constrain the element type so you can't insert type A and try to retrieve type B.
But generics were not added until CLR2/C#2. So arrays had to provide type safety in their own way.
Even so, it's not that different to generics. You note that there is no special class for int[]. But nor would there be for Array<int>. In generics there would only be the generic class Array<T>, and the CLR "magically" creates specialised versions for distinct type argument you use. So it would be no less "magic" if generics were used.
Despite this, in the CLR the type of any object is reified (it exists as a value you can manipulate), of type Type, and can be obtained with typeof. So although there is no code declaration of any array type (and why would you need to see it?) there is a Type object that you can query.
By the way, there was a design flaw in the way arrays constrain element types. You can declare an array:
int[] ints = ...
You can then store it in a looser variable:
object[] objs = ints;
But that means you can insert a string (at least it appears so at compile time):
objs[3] = "Oh dear";
At runtime it throws an exception. The idea of static type checking is to catch this kind of thing at compile time, not runtime. Generics would not have had this problem because they don't give assignment compatibility to generic class instances based on the compatibility of their type parameters. (Since C#4/CLR4 they have gained the ability to do that where it makes sense, but that wouldn't make sense for a mutable array.)
Look at the Array class.
When declaring an array using the [] syntax, the compiler, behind the scenes will use this class for you.
For C#, [] becomes a type that inherits from System.Array.
From the C# 4.0 spec:
§12.1.1 The System.Array type
The type System.Array is the abstract base type of all array types. An implicit reference conversion (§6.1.6) exists from any array type to System.Array, and an explicit reference conversion (§6.2.4) exists from System.Array to any array type. Note that System.Array is not itself an array-type. Rather, it is a class-type from which all array-types are derived.
There is such class. You cannot inherit it, but when you write "int[]" the compiler creates a type that inherits System.Array. So if you declare a variable:
int[] x;
This variable will have a type that inherits System.Array, and therefore has all its methods and properties.
This is also similar to delegates. When you define a delegate:
delegate void Foo(int x);
delegate int Bar(double x);
Then the type Foo is actually a class that inherits System.MulticastDelegate and Bar is a class that inherits System.Delegate.
I would recommend getting the ECMA 335 spec and looking for Arrays if you want to know the low level detail: http://www.ecma-international.org/publications/standards/Ecma-335.htm
I went digging through the ECMA 335 spec, so I figured I'd share what I read.
Exact array types are created automatically by the VES when they are required. Hence, the
operations on an array type are defined by the CTS. These generally are: allocating the array
based on size and lower-bound information, indexing the array to read and write a value,
computing the address of an element of the array (a managed pointer), and querying for the rank,
bounds, and the total number of values stored in the array.
The VES creates one array type for each
distinguishable array type.
Vectors are subtypes of System.Array, an abstract class pre-defined by the CLI. It provides several
methods that can be applied to all vectors. See Partition IV.
While vectors (§II.14.1) have direct support through CIL instructions, all other arrays are supported by
the VES by creating subtypes of the abstract class System.Array (see Partition IV)
While vectors (§II.14.1) have direct support through CIL instructions, all other arrays are supported by
the VES by creating subtypes of the abstract class System.Array (see Partition IV)
The class that the VES creates for arrays contains several methods whose implementation is supplied
by the VES:
It goes on to state, quite verbosely, that the methods supplied are:
Two constructors
Get
Set
Address (returns a managed pointer)
VES means Virtual Execution System, and the CLR is an implementation of it.
The spec also details how to store the data of the array (contiguously in row-major order), what indexing is allowed in arrays (0-based only), when a vector is created (single dimensional, 0-based arrays) as opposed to a different array type, when the CIL instruction newarr is used as opposed to newobj (creating a 0-based, single dimensional array).
Basically everything that the compiler has to do to build the method lookup tables etc.. for a regular type, it has to do for arrays, but they just programmed a more versatile and slightly special behavior into the compiler / JIT.
Why did they do it? Probably because arrays are special, widely used, and can be stored in an optimized fashion. The C# team did not necessarily make this decision though. It's more of a .NET thing, which is a cousin to Mono and Portable.NET, all of which are a CIL thing.
Arrays are special to CLR. They're allocated with 'newarr' instruction, and elements are accessed with 'ldelem*' and 'stelem*' instructions, not via System.Array methods;
see http://msdn.microsoft.com/en-us/library/system.reflection.emit.opcodes.newarr.aspx
You can check out ildasm output to see how arrays are represented.
So, to answer your question - no new type declaration is generated for any particular array.
[] is a syntax(syntatic sugar) for defining Arrays in c#. Maybe CreateInstance will be replaced at runtime
Array a = Array.CreateInstance(typeof(int), 5);
is same as
int[] a = new int[5];
Source for CreateInstance (taken from reflector)
public static unsafe Array CreateInstance(Type elementType, int length)
{
if (elementType == null)
{
throw new ArgumentNullException("elementType");
}
RuntimeType underlyingSystemType = elementType.UnderlyingSystemType as RuntimeType;
if (underlyingSystemType == null)
{
throw new ArgumentException(Environment.GetResourceString("Arg_MustBeType"), "elementType");
}
if (length < 0)
{
throw new ArgumentOutOfRangeException("length", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
}
return InternalCreate((void*) underlyingSystemType.TypeHandle.Value, 1, &length, null);
}