- Home /
Can I use generic objects as variables then cast them upon construction?
Say I have created the following library of functions:
public class MyClassFloat {
float foo;
float bar;
function MyClassFloat() {}
function UpdateBar() {
bar = foo * 2.0f;
}
}
public class MyClassVector3 {
Vector3 foo;
Vector3 bar;
function MyClassVector3() {}
function UpdateBar() {
bar = Vector3.Scale( foo, new Vector3(2.0f,2.0f,2.0f) );
}
}
And so on, with Vector2 and Quaternion. Can I instead do:
enum What( Float, Vector3 )
public class MyClass {
What what;
object foo;
object bar;
function MyClass( What w ) {
what = w;
if (w==What.Float) {
foo = float foo; // not sure of syntax to cast an object as a new type?
bar = float bar;
}
else if (w==What.Vector3) {
foo = new Vector3 foo;
bar = new Vector3 bar;
}
}
function UpdateBar() {
if (what==What.Float) {
bar = foo * 2.0f;
}
else if (what==What.Vector3) {
bar = Vector3.Scale( foo, new Vector3(2.0f,2.0f,2.0f) );
}
}
}
... in order to put them all into one generic class?
Answer by sneftel · Jun 16, 2011 at 07:46 PM
You can, yes, with some caveats.
First of all, float
is not itself a subclass of Object
. System.Single
, however, is. All primitive "value types" such as float
have "reference type" versions, which inherit from Object
and can be the subject of casting. These are known as "boxed" values.
Boxed values are immutable. You create one with, say, new Single(1.5f)
, and the returned object will always be equal to 1.5. If you want to change the value later, you do that by creating a new Single
and assigning it to the same variable. Different object, different quantity, same variable.
C# has a lot of automatic support for boxing and unboxing values, such that you can assign them directly from value types (with the creation happening automatically, behind the scenes), and even implicitly cast from Object
to value types (with the typechecking happening behind the scenes).
More info is available here.
Also, you don't really need the what
member variable; you can just directly check the types of the objects that foo
and bar
point to.
Incidentally, though, a whole lot of the stuff you're doing there seems like it might be better handled by generics. What is your specific application here? You can do what you want to do the way you're doing it here (more or less), but you'll end up doing a whoooole lotta boilerplate code. There are most likely more straightforward
Aha... I must have completely misunderstood the term Generics, I thought that's what object was! Thanks for pointing that out. So if I do the above with generics, I can create an instance with: $$anonymous$$yClass my = new $$anonymous$$yClass{float}(); ... or is it: $$anonymous$$yClass{Single} my = new $$anonymous$$yClass{Single}(); ??? Also, in the DoSomething() function, how do I check what type foo is, with generics?
By the way I used curly brackets ins$$anonymous$$d of less than/more than as using the latter formatted something in the comment and didn't show.
Well, that's how you instantiate a generic class, yes... though the type itself must also be qualified with the generic parameter. Of course, the class first needs to be written as a generic class.
I think I'm getting it now! Just trying it out. For finding which type an instance is, I'm using: if (foo.GetType().FullName=="System.Single") but is FullName an inefficient method to use? Does it do any boxing/unboxing? This will probably be called many times, so is there a faster test?
Answer by robinking · Jun 16, 2011 at 09:43 PM
Ben 12's answer above has provided me with a direction to head in, but I've encountered one problem I can't figure out:
public class Smooth<SM> {
public SM target;
public Smooth() {}
public void Inc(float i) {
if (target.GetType().FullName=="System.Single") {
target++;
}
else if (target.GetType().FullName=="UnityEngine.Vector3") {
target.x++;
}
}
}
The above (which I want to be able to instantiate as a float or a vector3) results in the following compile error:
error CS1061: Type SM' does not contain a definition for
x' and no extension method x' of type
SM' could be found (are you missing a using directive or an assembly reference?)
In fact even just this method: void Set(float a) { target=a; } results in the error: Cannot convert type S$$anonymous$$' to
float'... Surely generics allows for effecting changes in generic member variables?
You would have to cast target to a Vector3 as the generic type S$$anonymous$$
doesn't have an x
. You can cast it like this: Vector3 target = (Vector3) (object) target;
C# generics aren't really meant to handle what you are doing. They are meant to reduce the number of times similar code is written but as you can see you are having to write the same code multiple times to handle the different types making the generic somewhat irrelevant. Only pointing this out because you might run into more issues in the future.
Thanks Spencer. As you can see from the datestamp of the original question, it was a very much younger me that asked this! I understand generics (and indeed, polymorphism in general) a lot better now, and yes I wouldn't attack the problem I was trying to solve with generics.