I have a dictionary that stores different types of variables (string, double, int, etc.), they are stored as objects.
I have a generic Set method and a generic get method.
private static Dictionary<int, object> dict = new Dictionary<int, object>();
public static void Set<T>(int key, object value)
{
dict.Add(key, (T)value);
}
public static T Get<T>(int key)
{
dict.TryGetValue(key, out object value);
return (T)value;
}
static void Main(string[] args)
{
Set<int>(1, 100);
Get<double>(1);
}
In the Main I'm trying to save the variable 100 as integer, which works, it is stored "as an object" in the dictionary.
Now when trying to Get the variable, casting it to double, the IllegalCastException is thrown, but why?
A explicit cast from object to double is available, as well as a explicit cast from int to double.
You don't cast from object to int or to double. You unbox. And the unboxing rules are clear - you can only take out exactly the same type as you put in. (Insert some caveats around enums and underlying types which aren't exactly relevant to the question at hand)
For the unboxing of value types to succeed at run time, the item being unboxed must be a reference to an object that was previously created by boxing an instance of that value type. Attempting to unbox null causes a NullReferenceException. Attempting to unbox a reference to an incompatible value type causes an InvalidCastException.
Related
I often get confused with boxing and unboxing. I mean I understand that both of them mean casting from System.Object to a value type which inherits from it (e.g. System.Int32 or System.Double) or contrariwise. And casting from System.Object to a value type is unboxing, like
object o = 12; int i = (int)o;
and casting from a value type to System.Object is boxing, like
long l = 123L; object o = l;
But is it boxing or unboxing in this situation?
public class Program
{
public static void V(object o) => System.Console.WriteLine(o);
public static void Main(string[] args)
{
int i = 32;
V(i);
}
}
Boxing occurs because you are converting from a value type to a reference type. When you convert to object, you need to package the integer value into an object, so it can be stored on the heap.
You can use an analyzer like the ClrHeapAllocationAnalyzer to warn you in Visual Studio when boxing occurs in your application.
First: this is not a duplicate of How to check if an object is nullable?. Or, at least, there was no useful answer provided to that question, and the author's further elaboration actually asked how to determine whether a given type (eg. as returned from MethodInfo.ReturnType) is nullable.
However, that is easy. The hard thing is to determine whether a runtime object whose type is unknown at compile time is of a nullable type. Consider:
public void Foo(object obj)
{
var bar = IsNullable(obj);
}
private bool IsNullable(object obj)
{
var type = obj.GetType();
return type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>);
// Alternatively something like:
// return !(Nullable.GetUnderlyingType(type) != null);
}
This does not work as intended, because the GetType() call results in a boxing operation (https://msdn.microsoft.com/en-us/library/ms366789.aspx), and will return the underlying value type, rather than the nullable type. Thus IsNullable() will always return false.
Now, the following trick uses type parameter inference to get at the (unboxed) type:
private bool IsNullable<T>(T obj)
{
var type = typeof(T);
return type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>);
}
This seems initially promising. However, type parameter inference only works when the type of the object is known at compile time. So:
public void Foo(object obj)
{
int? genericObj = 23;
var bar1 = IsNullable(genericObj); // Works
var bar2 = IsNullable(obj); // Doesn't work (type parameter is Object)
}
In conclusion: the general problem is not to determine whether a type is nullable, but to get at the type in the first place.
So, my challenge is: how to determine if a runtime object (the obj parameter in the above example) is nullable? Blow me away :)
Well, you're too late. A boxed value no longer has the type information you seek - boxing a int? value results either in null, or a boxed int. In a way, it's an analogue to calling GetType on null - it doesn't really make sense, there's no type information.
If you can, stick with generic methods instead of boxing as much as possible (dynamic can be very helpful for some of the interfaces between actual nullable values and objects). If you can't, you'll have to use your own "boxing" - or even just creating your own Nullable-like type, that will be a class rather than a very hacky struct-like thing :D
If needed, in fact, you can even make your Nullable type a struct. It's not the struct-ness that breaks the type information - it's not the boxing itself that destroys that information, it's the CLR hacks that enable Nullable to match the performance of non-nullable values. It's very smart and very useful, but it breaks some reflection-based hacks (like the one you're trying to do).
This works as expected:
struct MyNullable<T>
{
private bool hasValue;
private T value;
public static MyNullable<T> FromValue(T value)
{
return new MyNullable<T>() { hasValue = true, value = value };
}
public static implicit operator T (MyNullable<T> n)
{
return n.value;
}
}
private bool IsMyNullable(object obj)
{
if (obj == null) return true; // Duh
var type = obj.GetType().Dump();
return type.IsGenericType
&& type.GetGenericTypeDefinition() == typeof(MyNullable<>);
}
Doing the same with System.Nullable doesn't; even just doing new int?(42).GetType() gives you System.Int32 instead of System.Nullable<System.Int32>.
System.Nullable not a real type - it gets special treatment by the runtime. It does stuff that you simply can't replicate with your own types, because the hack isn't even in the type definition in IL - it's right in the CLR itself. Another nice abstraction leak is that a nullable type is not considered a struct - if your generic type constraint is struct, you can't use a nullable type. Why? Well, adding this constraint means it's legal to use e.g. new T?() - which wouldn't be possible otherwise, since you can't make a nullable of a nullable. It's easy with the MyNullable type I've written here, but not with the System.Nullable.
EDIT:
The relevant part of the CLI specification (1.8.2.4 - Boxing and unboxing a value):
All value types have an operation called box. Boxing a value of any
value type produces its boxed value; i.e., a value of the
corresponding boxed type containing a bitwise copy of the original
value. If the value type is a nullable type—defined as an
instantiation of the value type System.Nullable—the result is a
null reference or bitwise copy of its Value property of type T,
depending on its HasValue property (false and true, respectively). All
boxed types have an operation called unbox, which results in a managed
pointer to the bit representation of the value.
So by definition, the box operation on nullable types produces either a null reference or the stored value, never a "boxed nullable".
What I have read, passing arguments is by default valuetypes. In my example the first function test1 takes a reference type and unbox, it will decrease the performance if I got this right.
However I have never read that you do like test2 for increase performance.
So whats best practice?
public Main(){
string test = "hello";
test1(test); // Does this line perform a boxing? So it's not good for performance?
test2(ref test); // Passing a reference as a reference
}
public string test1(string arg1) {
return arg1;
}
public string test2(ref string arg1) {
return arg1;
}
There's no boxing or unboxing involved at all here. string is a reference type - why would it be boxed? What would that even mean?
Even if you used int instead, there'd be no need for boxing, because there's no conversion of the value into an actual object.
I suspect your understanding of both boxing and parameter passing is flawed.
Boxing occurs when a value type value needs to be converted into an object, usually in order for it to be used as a variable (somewhere) of an interface or object type. So this boxes:
int value = 10;
Foo(value);
...
public void Foo(object x)
{
}
... but it wouldn't occur if Foo were changed such that the type of x were int instead.
The detailed rules on boxing become very complicated to state precisely and accurately, particularly where generics come in, but that's the basics.
There is no boxing here at all; boxing is when a value type is treated as object or an interface (not including generics), for example:
int i = 1;
Foo(i); // the value of i is boxed
Bar(i); // the value of i is boxed
...
private void Foo(object obj) {...}
private void Bar(IConvertible obj) {...}
In your examples, a: there is no type conversion here, so no need to box, and b: string is a reference-type anyway, so there is no meaning of boxing a string.
Your test2 is actually showing "pass by reference", aka ref, which is completely unrelated to boxing - and indeed ref parameters must be an exact match, so there is never any boxing involved in a ref parameter (however, subsequent code could obtain the value from the reference and then box/unbox that)
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Is it safe for structs to implement interfaces?
Take this code:
interface ISomeInterface
{
public int SomeProperty { get; }
}
struct SomeStruct : ISomeInterface
{
int someValue;
public int SomeProperty { get { return someValue; } }
public SomeStruct(int value)
{
someValue = value;
}
}
and then I do this somewhere:
ISomeInterface someVariable = new SomeStruct(2);
is the SomeStruct boxed in this case?
Jon's point is true, but as a side note there is one slight exception to the rule; generics. If you have where T : ISomeInterface, then this is constrained, and uses a special opcode. This means the interface can be used without boxing. For example:
public static void Foo<T>(T obj) where T : ISomeInterface {
obj.Bar(); // Bar defined on ISomeInterface
}
This does not involve boxing, even for value-type T. However, if (in the same Foo) you do:
ISomeInterface asInterface = obj;
asInterface.Bar();
then that boxes as before. The constrained only applies directly to T.
Yes, it is. Basically whenever you need a reference and you've only got a value type value, the value is boxed.
Here, ISomeInterface is an interface, which is a reference type. Therefore the value of someVariable is always a reference, so the newly created struct value has to be boxed.
I'm adding this to hopefully shed a little more light on the answers offered by Jon and Marc.
Consider this non-generic method:
public static void SetToNull(ref ISomeInterface obj) {
obj = null;
}
Hmm... setting a ref parameter to null. That's only possibly for a reference type, correct? (Well, or for a Nullable<T>; but let's ignore that case to keep things simple.) So the fact that this method compiles tells us that a variable declared to be of some interface type must be treated as a reference type.
The key phrase here is "declared as": consider this attempt to call the above method:
var x = new SomeStruct();
// This line does not compile:
// "Cannot convert from ref SomeStruct to ref ISomeInterface" --
// since x is declared to be of type SomeStruct, it cannot be passed
// to a method that wants a parameter of type ref ISomeInterface.
SetToNull(ref x);
Granted, the reason you can't pass x in the above code to SetToNull is that x would need to be declared as an ISomeInterface for you to be able to pass ref x -- and not because the compiler magically knows that SetToNull includes the line obj = null. But in a way that just reinforces my point: the obj = null line is legal precisely because it would be illegal to pass a variable not declared as an ISomeInterface to the method.
In other words, if a variable is declared as an ISomeInterface, it can be set to null, pure and simple. And that's because interfaces are reference types -- hence, declaring an object as an interface and assigning it to a value type object boxes that value.
Now, on the other hand, consider this hypothetical generic method:
// This method does not compile:
// "Cannot convert null to type parameter 'T' because it could be
// a non-nullable value type. Consider using 'default(T)' instead." --
// since this method could take a variable declared as, e.g., a SomeStruct,
// the compiler cannot assume a null assignment is legal.
public static void SetToNull<T>(ref T obj) where T : ISomeInterface {
obj = null;
}
The MSDN documentation tells us that structs are value, not reference types. They are boxed when converting to/from a variable of type object. But the central question here is: what about a variable of an interface type? Since the interface can also be implemented by a class, then this must be tantamount to converting from a value to a reference type, as Jon Skeet already said, therefore yes boxing would occur. More discussion on an msdn blog.
I have a generic function that is constrained to struct. My inputs are boxed ("objects"). Is it possible to unbox the value at runtime to avoid having to check for each possible type and do the casts manually?
See the above example:
public struct MyStruct
{
public int Value;
}
public void Foo<T>(T test)
where T : struct
{
// do stuff
}
public void TestFunc()
{
object o = new MyStruct() { Value = 100 }; // o is always a value type
Foo(o);
}
In the example, I know that o must be a struct (however, it does not need to be MyStruct ...). Is there a way to call Foo without tons of boilerplate code to check for every possible struct type?
Thank you.
.NET Generics are implemented in a manner that allows value types as a generic type parameter without incurring any boxing/unboxing overhead. Because your're casting to object before calling Foo you don't take advantage of that, in fact you're not even taking advantage of generics at all.
The whole point of using generics in the first place is to replace the "object-idiom". I think you're missing the concept here.
Whatever type T happens to be, it is available at run-time and because you constrained it to struct guaranteed to be a struct type.
Your TestFunc could be written like this without problem:
public void TestFunc()
{
MyStruct o = new MyStruct() { Value = 100 }; // o is always a value type
Foo<MyStruct>(o);
}
Looking at Foo, it would look like this in your example:
public void Foo<T>(T test)
where T : struct
{
T copy = test; // T == MyStruct
}
EDIT:
Ok, since the OP clarified what he wants to call the generic method but doesn't know the type of his struct (it's just object). The easiest way to call your generic method with the correct type parameter is to use a little reflection.
public void TestFunc()
{
object o = new DateTime();
MethodInfo method = this.GetType().GetMethod("Foo");
MethodInfo generic = method.MakeGenericMethod(o.GetType());
generic.Invoke(this, new object[] {o});
}
public void Foo<T>(T test)
where T : struct
{
T copy = test; // T == DateTime
}
No; you're using object, which is (by definition) not a struct/value type. Why are you intentionally boxing the value in this way?
The whole point of using generics is to avoid situations like this.
When you actually "close" the generic with a type of struct, you eliminate the need for runtime type checking: ie.
Foo<MyStruct>(MyStruct test);
Your implementation of Foo, can safely assume that it's dealing with a struct.
(Marked as CW because you can't pass an instance of ValueType to a generic requiring a struct, but it might be helpful for others who come across this question).
Instead of declaring o as an object, you can use a type of System.ValueType, which can only be assigned struct values; you cannot store an object in a ValueType.
However, I'm honestly not sure if that does anything in terms of (un)boxing. Note that ECMA-334 11.1.1 says:
System.ValueType is not itself a value-type. Rather, it is a class-type from which all value-types are automatically derived.
I dont know exactly what you are trying to archieve, but you could pass a delegate/lambda to unbox the value, and select some value in the struct you are interested in:
(Updated this code snippet after slurmomatics comment)
public void Foo<TValue>(object test, Func<object, TValue> ValueSelector)
where TValue : struct
{
TValue value = ValueSelector(test);
// do stuff with 'value'
}
public void TestFunc()
{
object o = new MyStruct() { Value = 100 };
// Do the unboxing in the lambda.
// Additionally you can also select some
// value, if you need to, like in this example
Foo(o, x => ((MyStruct)x).Value);
}
Update:
Then do this:
public static void Foo<TUnboxed>(object test)
where TUnboxed : struct
{
try
{
TUnboxed unboxed = (TUnboxed)test;
}
catch (InvalidCastException ex)
{
// handle the exception or re-throw it...
throw ex;
}
// do stuff with 'unboxed'
}
public void TestFunc()
{
// box an int
object o = 100;
// Now call foo, letting it unbox the int.
// Note that the generic type can not be infered
// but has to be explicitly given, and has to match the
// boxed type, or throws an `InvalidCastException`
Foo<int>(o);
}