According to C# specification in 10.4 Constants:
The type specified in a constant declaration must be sbyte, byte,
short, ushort, int, uint, long, ulong, char, float, double, decimal,
bool, string, an enum-type, or a reference-type. Each
constant-expression must yield a value of the target type or of a type
that can be converted to the target type by an implicit conversion
(§6.1).
Why then I can't do following:
public class GenericClass<T>
where T : class
{
public const T val = null;
}
That should be possible, because:
where T : class means, that The type argument must be a reference type; this applies also to any class, interface, delegate, or array type (from MSDN)
it satisfies another words from specification: the only possible value for constants of reference-types other than string is null.
Any possible explanation?
Possible Explanation
Consider how the CLR initializes static members of generic classes or when it invokes static constructors on generic types. Normally, static initialization occurs when the program is first loaded; however, generic classes initialize their static members the first time an instance of that class is created.
Bear in mind that a generic class is not a single type; every single T that gets passed in the type declaration is creating a new type.
Now then consider a const expression, which has the requirement to be evaluated at compile-time. Although T is constrained as a class, and therefore it can receive the value null, the variable val does not exist in memory until the class has been created at runtime.
For example, consider if the const T val were valid. Then elsewhere in the code we could use:
GenericClass<string>.val
GenericClass<object>.val
Edit
Although both expressions would have the value null, the former is of type string and the latter is of type object. In order for the compiler to perform substitution, it needs to know the type definitions of the constants in question.
Constraints may be enforced at compile-time, but open generics are not converted into closed generics until runtime. Therefore, GenericClass<object>.val cannot be stored in the compiler's local memory to perform the substitution because the compiler does not instantiate the closed form of the generic class, and thus does not know what type to instantiate the constant expression to.
Eric Lippert admitted it is a bug, and it should be allowed:
It looks to me like you’ve found a bug; either the bug is in the specification, which should explicitly call out that type parameters are not valid types, or the bug is in the compiler, which should allow it.
Related
I have an attribute that I'm using params in the constructor and I want to pass a class to it in the params.
public class FooAttribute: SomeAttribute
{
public FooAttribute(params Foo[] foo)
{
// some code
}
}
public abstract class Foo
{
public Foo(int #int, string name)
{
// code
}
// more code
}
And this doesn't work, when using the attribute
[AuthorizeFeature(FooHelper.X, FooHelper.Y)]
(FooHelper is a static class that instantiate Foo with specific constructor parameters)
I get the following error
Error CS0181 Attribute constructor parameter 'foo' has type 'Foo[]', which is not a valid attribute parameter type
But why it doesn't work? While testing, I changed the type Foo for string and it work, so I should be able to pass a class with no problems right?
Or maybe I can't pass a abstract class? Or can't pass a class with parameters in the constructor? Or is there something else that is causing this error? I can't figure it out...
The parameters to an Attribute constructor must be constants, or Types, or 1-D Arrays of them.
You can't use anything that's merely static readonly. So you're mostly limited to string, number literals and constants, enums and Type. You can't even use Decimal.
A workaround is for the parameters to your Attribute constructor to be the parameters you can use in your Attribute constructor's code to create its own instance.
NB another common gotcha with Attributes is that they sometimes turn out to be singletons-per-set-of-parameters. You get little control of Attributes.
An expression E is an attribute_argument_expression if all of the following statements are true:
The type of E is an attribute parameter type.
At compile-time, the value of E can be resolved to one of the following:
A constant value.
A System.Type object.
A one-dimensional array of attribute_argument_expressions.
https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/attributes
and
The types of positional and named parameters for an attribute class are limited to the attribute parameter types, which are:
One of the following types: bool, byte, char, double, float, int, long, sbyte, short, string, uint, ulong, ushort.
The type object.
The type System.Type.
An enum type, provided it has public accessibility and the types in which it is nested (if any) also have public accessibility (Attribute specification).
Single-dimensional arrays of the above types.
A constructor argument or public field which does not have one of these types, cannot be used as a positional or named parameter in an attribute specification.
https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/attributes#attribute-parameter-types
Here is my code
public class UnOrderedMaxPQ<Key> where Key : IComparable
{
private Key[] array;
private int N;
public UnOrderedMaxPQ(int size)
{
array = new Key[size];
}
.......
public Key DelMax()
{
InsertionSort.Sort(array);//Error cannot convert from 'Key[]' to IComparable[]'
return array[--N];
}
}
And here is insertion sort code
public class InsertionSort
{
public static void Sort(IComparable[] array)
{
for (int i = 0; i < array.Length-1; i++)
{
for (int j = i + 1; j <=0;j--)
{
if (Less(array[j], array[i]))
{
Exchange(array, j, i);
}
else
break;
}
}
}
So my question is when I have constrained the "Key" to be IComparable, why can't I pass it to Sort method which takes IComparable as parameter? What changes I have to make to make it work?
You are trying to take advantage of array covariance -- the idea that you can substitute an array of a derived type when an array of a base type is required. However, in c#, this only works for reference types. From the docs:
For any two reference-types A and B, if an implicit reference conversion (Section 6.1.4) or explicit reference conversion (Section 6.2.3) exists from A to B, then the same reference conversion also exists from the array type A[R] to the array type B[R], where R is any given rank-specifier (but the same for both array types). This relationship is known as array covariance. Array covariance in particular means that a value of an array type A[R] may actually be a reference to an instance of an array type B[R], provided an implicit reference conversion exists from B to A.
Thus if you constrain your Key type to a class -- a reference type -- your code will compile:
public class UnOrderedMaxPQ<Key> where Key : class, IComparable
{
}
Of course, this means you wouldn't be able to use a value type such as int as a Key, so you probably don't want to do this.
Alternatively, you could eliminate the need for array covariance by declaring your Sort method (and the methods Exchange and Less) to be generic also:
public class InsertionSort
{
public static void Sort<TComparable>(TComparable[] array) where TComparable : IComparable
{
}
private static void Exchange<TComparable>(TComparable[] array, int j, int i) where TComparable : IComparable
{
}
private static bool Less<TComparable>(TComparable iComparable, TComparable iComparable_2) where TComparable : IComparable
{
}
}
Now your code will compile and work for both reference and value types. This solution also avoids boxing of reference types, as is explained here: Benefits of Generics (C# Programming Guide).
Update
One reason that array covariance does not work for value types, but generic methods do work, is that value types can have different sizes but reference type references all have the same size. All reference type variables TComparable foo occupy 4 (32 bit) or 8 (64 bit) bytes on stack no matter the specific type of TComparable (though of course the managed memory to which they refer can occupy any size). But a comparable value type, say DateTime, may have a different size on the stack than another comparable value type, e.g. Int16. Thus MSIL generated for one need not work for the other.
Generics handle this difference nicely as is described here:
Generics in the Run Time (C# Programming Guide)
When a generic type or method is compiled into Microsoft intermediate language (MSIL), it contains metadata that identifies it as having type parameters. How the MSIL for a generic type is used differs based on whether the supplied type parameter is a value type or reference type.
When a generic type is first constructed with a value type as a parameter, the runtime creates a specialized generic type with the supplied parameter or parameters substituted in the appropriate locations in the MSIL. Specialized generic types are created one time for each unique value type that is used as a parameter.
...
Generics work somewhat differently for reference types. The first time a generic type is constructed with any reference type, the runtime creates a specialized generic type with object references substituted for the parameters in the MSIL. Then, every time that a constructed type is instantiated with a reference type as its parameter, regardless of what type it is, the runtime reuses the previously created specialized version of the generic type. This is possible because all references are the same size.
Incidentally, rather than constraining Key to implement IComparable, you might want to constrain it to implement IComparable<Key>. This guarantees type safety in your comparisons and also avoids boxing of value types.
I have the following code as part of a system for generating interfaces using reflection.emit
class Class1:Attribute
{
public Class1(XmlDocument doc)
{
}
}
var type = typeof(Class1);
var ctore = type.GetConstructor(new[] { typeof(XmlDocument) });
var cab = new CustomAttributeBuilder(ctore, new object[] { new XmlDocument() });
For reasons unknown to me, the program generates an error:
In an argument, field, or property used designer custom attribute type is invalid.
See remarks section of CustomAttributeBuilder documentation:
The elements of the constructorArgs array are restricted to element
types. They can be byte, sbyte, int, uint, long, ulong, float, double,
String, char, bool, an enum, a type, any of the previous types that
was cast to an object, or a single-dimension, zero-based array of any
of the previous types.
You cannot use XmlDocument type as a constructor argument, because its not in list. This restriction goes from C# attribute parameters restriction. See 17.1.3 Attribute parameter types section of C# specification for list of acceptable parameter types:
One of the following types: bool, byte, char, double, float, int, long, short, string.
The type object.
The type System.Type.
An enum type, provided it has public accessibility and the types in
which it is nested (if any) also have public accessibility (Section
17.2).
Single-dimensional arrays of the above types.
Constructor public Class1(XmlDocument doc) is completely valid for regular C# class, and you can declare it in attribute class. But you can't use it when you'll apply attribute to your code. And this is goal of any attribute class. So, despite you can declare such constructor, it makes no sense for attribute class.
I found a workaround. I will use the class XmlAttributeAttribute. Thank you.
Basically why is the following invalid in C#? I can find plenty of good uses for it and in fact can fix it by creating my own nullable struct class but why and how does the C# specification (and hence the compiler) prevent it?
The below is a partial example of what I'm talking about.
struct MyNullable<T> where T : struct
{
public T Value;
public bool HasValue;
// Need to overide equals, as well as provide static implicit/explit cast operators
}
class Program
{
static void Main(string[] args)
{
// Compiles fine and works as expected
MyNullable<Double> NullableDoubleTest;
NullableDoubleTest.Value = 63.0;
// Also compiles fine and works as expected
MyNullable<MyNullable<Double>> NullableNullableTest;
NullableNullableTest.Value.Value = 63.0;
// Fails to compile...despite Nullable being a struct
// Error: The type 'double?' must be a non-nullable value type in order to use it as parameter 'T' in the generic type or method 'ConsoleApplication1.MyNullable<T>'
MyNullable<Nullable<Double>> MyNullableSuperStruct;
}
}
It is a struct. It just doesn't satisfy the value type generic type parameter constraint. From 10.1.5 of the language specification:
The value type constraint specifies that a type argument used for the type parameter must be a non-nullable value type. All non-nullable struct types, enum types, and type parameters having the value type constraint satisfy this constraint. Note that although classified as a value type, a nullable type (§4.1.10) does not satisfy the value type constraint.
So, the where T : struct doesn't mean what you think it means.
Basically why is the following invalid in C#?
Because where T : struct can only be satisfied by T that are non-nullable value types. Nullable<TNonNullableValueType> does not satisfy this constraint.
why and how does the compiler prevent it?
Why? To be consistent with the specification. How? By performing syntactic and semantic analysis and determining that you've supplied a generic type parameter T that doesn't satisfy the generic type constraint where T : struct.
[I] can fix it by creating my own nullable struct class but
No, you're version doesn't fix it. It's basically exactly the same as Nullable<T> except you don't get special handling by the compiler, and you're going to cause some boxing that the compiler's implementation won't box.
I can find plenty of good uses for it
Really? Such as? Keep in mind, the basic idea of Nullable<T> is to have a storage location that can contain T or can represent "the value is missing." What's the point of nesting this? That is, what's the point of Nullable<Nullable<T>>? It doesn't even make conceptual sense. That might be why it's prohibited, but I'm merely speculating (Eric Lippert has confirmed that this speculation is correct). For example, what is an int??? It represents a storage location that represents the value is missing or is an int?, which is itself a storage location that represents the value is missing or is an int? What's the use?
One reason for the struct constraint's diallowing nullables is that we want to be able to use T? in generic methods. If struct permitted nullable value types, the compiler would have to prohibit T?.
The nullable type must have special handling in the compiler in other cases as well:
The null keyword must be implicitly convertible to a nullable type; this is impossible with a value type.
Nullable value types can be compared with the null keyword; with non-nullable value types, this comparison always returns false.
Nullable value types work with the ?? operator; non-nullables do not.
I've seen both terms be used almost interchangeably in various online explanations, and most text books I've consulted are also not entirely clear about the distinction.
Is there perhaps a clear and simple way of explaining the difference that you guys know of?
Type conversion (also sometimes known as type cast)
To use a value of one type in a context that expects another.
Nonconverting type cast (sometimes known as type pun)
A change that does not alter the underlying bits.
Coercion
Process by which a compiler automatically converts a value of one type into a value of another type when that second type is required by the surrounding context.
Type Conversion:
The word conversion refers to either implicitly or explicitly changing a value from one data type to another, e.g. a 16-bit integer to a 32-bit integer.
The word coercion is used to denote an implicit conversion.
The word cast typically refers to an explicit type conversion (as opposed to an implicit conversion), regardless of whether this is a re-interpretation of a bit-pattern or a real conversion.
So, coercion is implicit, cast is explicit, and conversion is any of them.
Few examples (from the same source) :
Coercion (implicit):
double d;
int i;
if (d > i) d = i;
Cast (explicit):
double da = 3.3;
double db = 3.3;
double dc = 3.4;
int result = (int)da + (int)db + (int)dc; //result == 9
Usages vary, as you note.
My personal usages are:
A "cast" is the usage of a cast operator. A cast operator instructs the compiler that either (1) this expression is not known to be of the given type, but I promise you that the value will be of that type at runtime; the compiler is to treat the expression as being of the given type, and the runtime will produce an error if it is not, or (2) the expression is of a different type entirely, but there is a well-known way to associate instances of the expression's type with instances of the cast-to type. The compiler is instructed to generate code that performs the conversion. The attentive reader will note that these are opposites, which I think is a neat trick.
A "conversion" is an operation by which a value of one type is treated as a value of another type -- usually a different type, though an "identity conversion" is still a conversion, technically speaking. The conversion may be "representation changing", like int to double, or it might be "representation preserving" like string to object. Conversions may be "implicit", which do not require a cast, or "explicit", which do require a cast.
A "coercion" is a representation-changing implicit conversion.
Casting is the process by which you treat an object type as another type, Coercing is converting one object to another.
Note that in the former process there is no conversion involved, you have a type that you would like to treat as another, say for example, you have 3 different objects that inherit from a base type, and you have a method that will take that base type, at any point, if you know the specific child type, you can CAST it to what it is and use all the specific methods and properties of that object and that will not create a new instance of the object.
On the other hand, coercing implies the creation of a new object in memory of the new type and then the original type would be copied over to the new one, leaving both objects in memory (until the Garbage Collectors takes either away, or both).
As an example consider the following code:
class baseClass {}
class childClass : baseClass {}
class otherClass {}
public void doSomethingWithBase(baseClass item) {}
public void mainMethod()
{
var obj1 = new baseClass();
var obj2 = new childClass();
var obj3 = new otherClass();
doSomethingWithBase(obj1); //not a problem, obj1 is already of type baseClass
doSomethingWithBase(obj2); //not a problem, obj2 is implicitly casted to baseClass
doSomethingWithBase(obj3); //won't compile without additional code
}
obj1 is passed without any casting or coercing (conversion) because it's already of the same type baseClass
obj2 is implicitly casted to base, meaning there's no creation of a new object because obj2 can already be baseClass
obj3 needs to be converted somehow to base, you'll need to provide your own method to convert from otherClass to baseClass, which will involve creating a new object of type baseClass and filling it by copying the data from obj3.
A good example is the Convert C# class where it provides custom code to convert among different types.
According to Wikipedia,
In computer science, type conversion, type casting, type coercion, and type juggling are different ways of changing an expression from one data type to another.
The difference between type casting and type coercion is as follows:
TYPE CASTING | TYPE COERCION
|
1. Explicit i.e., done by user | 1. Implicit i.e., done by the compiler
|
2. Types: | 2. Type:
Static (done at compile time) | Widening (conversion to higher data
| type)
Dynamic (done at run time) | Narrowing (conversion to lower data
| type)
|
3. Casting never changes the | 3. Coercion can result in representation
the actual type of object | as well as type change.
nor representation. |
Note: Casting is not conversion. It is just the process by which we treat an object type as another type. Therefore, the actual type of object, as well as the representation, is not changed during casting.
I agree with #PedroC88's words:
On the other hand, coercing implies the creation of a new object in
memory of the new type and then the original type would be copied over
to the new one, leaving both objects in memory (until the Garbage
Collectors takes either away, or both).
Casting preserves the type of objects. Coercion does not.
Coercion is taking the value of a type that is NOT assignment compatible and converting to a type that is assignment compatible. Here I perform a coercion because Int32 does NOT inherit from Int64...so it's NOT assignment compatible. This is a widening coercion (no data lost). A widening coercion is a.k.a. an implicit conversion. A Coercion performs a conversion.
void Main()
{
System.Int32 a = 100;
System.Int64 b = a;
b.GetType();//The type is System.Int64.
}
Casting allows you to treat a type as if it were of a different type while also preserving the type.
void Main()
{
Derived d = new Derived();
Base bb = d;
//b.N();//INVALID. Calls to the type Derived are not possible because bb is of type Base
bb.GetType();//The type is Derived. bb is still of type Derived despite not being able to call members of Test
}
class Base
{
public void M() {}
}
class Derived: Base
{
public void N() {}
}
Source: The Common Language Infrastructure Annotated Standard by James S. Miller
Now what's odd is that Microsoft's documentation on Casting does not align with the ecma-335 specification definition of Casting.
Explicit conversions (casts): Explicit conversions require a cast
operator. Casting is required when information might be lost in the
conversion, or when the conversion might not succeed for other
reasons. Typical examples include numeric conversion to a type that
has less precision or a smaller range, and conversion of a base-class
instance to a derived class.
...This sounds like Coercions not Casting.
For example,
object o = 1;
int i = (int)o;//Explicit conversions require a cast operator
i.GetType();//The type has been explicitly converted to System.Int32. Object type is not preserved. This meets the definition of Coercion not casting.
Who knows? Maybe Microsoft is checking if anybody reads this stuff.
From the CLI standard:
I.8.3.2 Coercion
Sometimes it is desirable to take a value of a type that is not assignable-to a location, and convert
the value to a type that is assignable-to the type of the location. This is accomplished through
coercion of the value. Coercion takes a value of a particular type and a desired type and attempts
to create a value of the desired type that has equivalent meaning to the original value. Coercion
can result in representation change as well as type change; hence coercion does not necessarily
preserve object identity.
There are two kinds of coercion: widening, which never loses information, and narrowing, in
which information might be lost. An example of a widening coercion would be coercing a value
that is a 32-bit signed integer to a value that is a 64-bit signed integer. An example of a
narrowing coercion is the reverse: coercing a 64-bit signed integer to a 32-bit signed integer.
Programming languages often implement widening coercions as implicit conversions, whereas
narrowing coercions usually require an explicit conversion.
Some coercion is built directly into the VES operations on the built-in types (see §I.12.1). All
other coercion shall be explicitly requested. For the built-in types, the CTS provides operations
to perform widening coercions with no runtime checks and narrowing coercions with runtime
checks or truncation, according to the operation semantics.
I.8.3.3 Casting
Since a value can be of more than one type, a use of the value needs to clearly identify which of
its types is being used. Since values are read from locations that are typed, the type of the value
which is used is the type of the location from which the value was read. If a different type is to
be used, the value is cast to one of its other types. Casting is usually a compile time operation,
but if the compiler cannot statically know that the value is of the target type, a runtime cast check
is done. Unlike coercion, a cast never changes the actual type of an object nor does it change the
representation. Casting preserves the identity of objects.
For example, a runtime check might be needed when casting a value read from a location that is
typed as holding a value of a particular interface. Since an interface is an incomplete description
of the value, casting that value to be of a different interface type will usually result in a runtime
cast check.
Below is a posting from the following article:
The difference between coercion and casting is often neglected. I can see why; many languages have the same (or similar) syntax and terminology for both operations. Some languages may even refer to any conversion as “casting,” but the following explanation refers to concepts in the CTS.
If you are trying to assign a value of some type to a location of a different type, you can generate a value of the new type that has a similar meaning to the original. This is coercion. Coercion lets you use the new type by creating a new value that in some way resembles the original. Some coercions may discard data (e.g. converting the int 0x12345678 to the short 0x5678), while others may not (e.g. converting the int 0x00000008 to the short 0x0008, or the long 0x0000000000000008).
Recall that values can have multiple types. If your situation is slightly different, and you only want to select a different one of the value’s types, casting is the tool for the job. Casting simply indicates that you wish to operate on a particular type that a value includes.
The difference at the code level varies from C# to IL. In C#, both casting and coercion look fairly similar:
static void ChangeTypes(int number, System.IO.Stream stream)
{
long longNumber = number;
short shortNumber = (short)number;
IDisposable disposableStream = stream;
System.IO.FileStream fileStream = (System.IO.FileStream)stream;
}
At the IL level they are quite different:
ldarg.0
conv.i8
stloc.0
ldarg.0
conv.i2
stloc.1
ldarg.1
stloc.2
ldarg.1
castclass [mscorlib]System.IO.FileStream
stloc.3
As for the logical level, there are some important differences. What’s most important to remember is that coercion creates a new value, while casting does not. The identity of the original value and the value after casting are the same, while the identity of a coerced value differs from the original value; coersion creates a new, distinct instance, while casting does not. A corollary is that the result of casting and the original will always be equivalent (both in identity and equality), but a coerced value may or may not be equal to the original, and never shares the original identity.
It’s easy to see the implications of coercion in the examples above, as the numeric types are always copied by value. Things get a bit trickier when you’re working with reference types.
class Name : Tuple<string, string>
{
public Name(string first, string last)
: base(first, last)
{
}
public static implicit operator string[](Name name)
{
return new string[] { name.Item1, name.Item2 };
}
}
In the example below, one conversion is a cast, while the other is a coercion.
Tuple<string, string> tuple = name;
string[] strings = name;
After these conversions, tuple and name are equal, but strings is not equal to either of them. You could make the situation slightly better (or slightly more confusing) by implementing Equals() and operator ==() on the Name class to compare a Name and a string[]. These operators would “fix” the comparison issue, but you would still have two separate instances; any modification to strings would not be reflected in name or tuple, while changes to either one of name or tuple would be reflected in name and tuple, but not in strings.
Although the example above was meant to illustrate some differences between casting and coercion, it also serves as a great example of why you should be extremely cautious about using conversion operators with reference types in C#.