Boxing

Can someone help me understand boxing? I'm struggling to understand why using IFoo<T> where T: struct, IFoo<T> would be an advantage over using T directly, but If I make an IFoo it gets boxed Is there some way to prevent boxing while still using multiple types of IFoo<T>?
33 Replies
FusedQyou
FusedQyou4d ago
I don't know what your example means Do you know the difference between a reference type and a value type, or a class and a struct?
actual_weeb_tm
actual_weeb_tmOP4d ago
More or less, what I want to do is implement a class that works with both a generic and non-generic interface and a property that implements another generic interface, but still avoid boxing of the struct.
Anton
Anton4d ago
There's no difference for classes, just use the interface if you can. It's gonna compile to basically the same code For structs, you need to pass a concrete implementation to a method with a generic so the jit can recompile the method if you just accept an interface, it's going to box, always
actual_weeb_tm
actual_weeb_tmOP4d ago
Yeah I'm looking to do this with structs though, which preferably I'd keep, but it seems there's no way to abstract away the generic T, apart from writing a factory method, I suppose
Anton
Anton4d ago
let me understand
actual_weeb_tm
actual_weeb_tmOP4d ago
From what I've read it seems an IFoo<T> will still be kept unboxed, is that not correct?
Anton
Anton4d ago
is your IFoo something like object Deserialize(string);? and you generic IFoo is like T Deserialize(string)? no you need to accept a T interfaces are always reference types you accept a T that implements an interface which you indicate in the constraint that's fine using methods from IFoo via a T is fine
actual_weeb_tm
actual_weeb_tmOP4d ago
Right yeah okay, I got it
Anton
Anton4d ago
but the second you convert it to an IFoo, it's going to get boxed nothing you can do about it
actual_weeb_tm
actual_weeb_tmOP4d ago
Right, I think I get it, thanks.
Anton
Anton4d ago
np it would make sense honestly but I don't think that's how it works you should check it with sharp lab to confirm
actual_weeb_tm
actual_weeb_tmOP4d ago
means I can still write a factory method that returns an IBar which in turn implements a specific IFoo<something> Bit of a roundabout way to do it but it should work Well for my specific case I now have an IVector<T> where T: struct, IVector<T> Which should keep the IVector<Foo> unboxed.
Anton
Anton4d ago
It would make sense, but I don't think it will if you pass it as IVector<T> or the method gets inlined
actual_weeb_tm
actual_weeb_tmOP4d ago
Yeah but I'm passing T directly, so it should be fine, As in, the next class up in the hierarchy is
Segment<T> where T: IVector<T>
Segment<T> where T: IVector<T>
Anton
Anton4d ago
show the method signature where you pass it the vector
actual_weeb_tm
actual_weeb_tmOP4d ago
So basically just using it as a constraint. I think I can include a factory method that instantiates this with the proper implementation of the interface. It's a bit of a roundabout way to do it but this allows me to use the built in optimised Vector2 and Vector3 while still allowing my own less optimised VectorN for higher dimensions Should save on performance in the end
Anton
Anton4d ago
or maybe the whole method
actual_weeb_tm
actual_weeb_tmOP4d ago
public Segment(T position)
public Segment(T position)
Anton
Anton4d ago
ok system.numerics? aren't those for simd specifically?
actual_weeb_tm
actual_weeb_tmOP4d ago
yes, I think they should be faster and if they're not it's easy enough to swap them out for another implementation All I know about SIMD is that it's supposed to be better when parallel processing large datasets which is what I'll end up doing lol
333fred
333fred4d ago
To give you another way to think about it, the method needs to know the size of the variable being passed in. If it knows there's a T, then it can be specialized for every T. If it just knows there's an IFoo, then it isn't specialized for every T, and therefore all it can do is accept a pointer, where that pointer is to the heap Yes, in theory, we could just point to the stack, but there's no lifetime constraints on interfaces like that so it's not safe to do so. The interface could be saved to a field, for example, and then suddenly you have a field pointing at invalid memory
Anton
Anton4d ago
You'll likely need to use the simd instructions to enable them, because the compiler will have a difficult time seeing optimizable code through your abstractions otherwise you know the exact type there, because of the way the constaint is set up
333fred
333fred4d ago
What?
Anton
Anton4d ago
IFoo<T> will always have to be a T IFoo<T> where T :struct, IFoo<T> you can deduce at compile time that if you pass an IFoo<T>, it will always have to be a T because of the self reference, right?
333fred
333fred4d ago
No
Anton
Anton4d ago
in theory
333fred
333fred4d ago
No I mean, in practice, we'd tell you that you did a dumb by implementing IFoo<SomeOtherIFoo> On SomeFirstFoo But there's nothing preventing you from doing that
Anton
Anton4d ago
you can't due to the constraint though
333fred
333fred4d ago
Yes you can
Anton
Anton4d ago
how though I can't think of an example
MODiX
MODiX4d ago
333fred
sharplab.io (click here)
M(new C());
void M<T>(I<T> i) where T : I<T> {}
interface I<T> where T : I<T> {}
public class B : I<B> {}
public class C : I<B> {
public void M() {
}
}
M(new C());
void M<T>(I<T> i) where T : I<T> {}
interface I<T> where T : I<T> {}
public class B : I<B> {}
public class C : I<B> {
public void M() {
}
}
React with ❌ to remove this embed.
333fred
333fred4d ago
Again, if you were to do this in practice, you did a dumb But you can do it
Anton
Anton4d ago
right thanks for the example
Want results from more Discord servers?
Add your server