Is it boxing or unboxing? - c#

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.

Related

Why does this explicit cast not work when there is an implicit conversion defined?

I have a class with a defined implicit conversion to int:
public class SpeakerId
{
private int value;
public SpeakerId(int value)
{
this.value = value;
}
//integer conversions
public static implicit operator SpeakerId(int value) => new SpeakerId(value);
public static implicit operator int(SpeakerId obj) => obj.value;
...
}
With those conversions operators, I would think that this code would work:
var value = new SpeakerId(3);
var speakerId = (int)value;
but it doesn't; it causes a Specified cast is not valid error. Why would that be?
Update:
My original code sample does work. The place where I get the error is when there's an intermediate cast to object, as you would have in a value converter:
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var speakerId = (int)value;
return ...
}
or, easier to see:
var value = new SpeakerId(3);
object intermediate = value;
var speakerId = (int)intermediate;
So (int) against an object is unboxing, not casting, and doesn't invoke any conversion operators1. You need to cast your object back to your reference type before you can try applying (int) to act as an actual cast:
var speakerId = (int)(SpeakerId)value;
Or you need to ensure you cast to int before it gets converted to object so that it is a boxed int and can be unboxed.
Alternatively, if you don't know what the type of value is going to be but you're "sure" it'll have an appropriate conversion operator defined, you can force the lookup for a conversion to happen at runtime via dynamic:
var speakerId = (int)(dynamic)value;
1Except for some oddities around enums and their base types, unboxing has to be exactly to the type that was originally boxed.

Generic Cast, casting a int to double not possible

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.

Passing arguments, does unboxing occur

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)

c#4.0: int a real subtype of object? covariance, ienumerable and value types

I wonder why IEnumerable<int> can't be assigned to a IEnumerable<object>. After all IEnumerable is one of the few interfaces that supports covariance...
The subtype relation and covariance stuff works with reference types
int seems to be a proper subtype of object
The combination of both features doesn't work however...
class A
{
}
class B : A
{
}
class Program
{
static void Main(string[] args)
{
bool b;
b = typeof(IEnumerable<A>).IsAssignableFrom(typeof(List<B>));
Console.WriteLine("ienumerable of ref types is covariant: " + b); //true
b = typeof(IEnumerable<object>).IsAssignableFrom(typeof(List<int>));
Console.WriteLine("ienumerable of value tpyes is covariant: " + b); //false
b = typeof(object).IsAssignableFrom(typeof(int));
Console.WriteLine("int is a subtype of object: " + b); //true
}
}
thanks for your help!
sebastian
Value types aren't LSP-subtypes of object until they're boxed.
Variance doesn't work with value types. At all.
Demonstration that int is not a proper subtype (subtype in the LSP sense) of object:
Works:
object x = new object();
lock (x) { ... }
Does not work (substitutability violated):
int y = new int();
lock (y) { ... }
Returns true:
object x = new object();
object a = x;
object b = x;
return ReferenceEquals(a, b);
Returns false (substitutability violated):
int y = new int();
object a = y;
object b = y;
return ReferenceEquals(a, b);
Of course, the topic of the question (interface variance) is a third demonstration.
The problem is that object is a reference type, not a value type. The only reason you can assign an int to a variable of type object is boxing.
In order to assign List<int> to IEnumerable<object> you'd have to box each element of the list. You can't do that just by assigning the reference to the list and calling it a different type.
The simplistic answer is that this is just one of the quirks in the way that variance is implemented in C# and the CLR.
From "Covariance and Contravariance in Generics":
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.
Every value type in .net has a corresponding ("boxed") object type. Non-boxed value types are effectively outside the object type hierarchy, but the compiler will perform a widening from the value type to the boxed class type. It would be helpful to have a "class" Boxed<T> which would support a widening conversions to and from T, but which would be a class type. Internally, I think that's what the compiler's doing implicitly, but I don't know any way to do it explicitly. For any particular type like "integer", there would be no difficulty defining a class which would behave as a Boxed<Integer> should, but I don't know any way of doing such a thing in generic fashion.

Generic unboxing of boxed value types

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);
}

Categories

Resources