简体   繁体   中英

Adding Struct : Interface to a List<Interface>

I need to pass structs as value types in a list. Suppose I have a struct which derives from an interface:

interface IFoo
{
    ...
}
struct Foo1 : IFoo { ... }
struct Foo2 : IFoo { ... }

//Some other class which contains this:
List<IFoo> listOfFoo;

I know if I did it like so: IFoo foo = new Foo1() , it would turn the value into a reference (boxing).

  1. Would the structs not be passed as a reference if I added Foo1 or Foo2 to List<IFoo> ?
  2. If not, is it safe to do List<object> and only add in these structs, or would it be better to do MemberwiseClone on a class?

I'm also looking for efficiency, as this will be for collision detection in a tile map.

  1. The structs would be boxed. Why wouldn't they be? Every value in List<IFoo> must be an IFoo , so each struct instance you add is converted -- through boxing.

  2. The structs are still boxed because object is a reference type as well. In your scenario, there is simply no way to avoid boxing unless you declare the list to be of a specific value type ( List<Foo1> or List<Foo2> ).

In general, using structs for "efficiency" is not at all a simple or obvious thing. In particular, simply chucking in struct where you'd otherwise write class is not guaranteed to make your code perform better. Write your code in the obvious way first (and obvious here means: using classes), then determine (through profiling) if you need to optimize it and if so, how.

If you have a List where I is an interface and you add elements of type F1 and F2 where both implement the I interface, then when you retrieve any of the two elements from the List you can check the type of the reference using the keyword **is ** and proceed to apply the proper cast to the element you got from the List.

For example:

struct Foo1 : IFoo {...}
struct Foo2 : IFoo {...}

List<IFoo> listOfFoo = new List<IFoo>();

IFoo foo1 = new Foo1();
IFoo foo2 = new Foo2();
listOfFoo.Add(foo1);
listOfFoo.Add(foo2);

// lets retrieve the first element and check if it's a Foo1 value type
if(listOfFoo[0] is Foo1){
    // cast element from List to Foo1
    Foo1 foo = (Foo1) listOfFoo[0];
}

Since we're dealing with structs, when the element from the List gets casted back to the original value type, it should be be unboxed. But too many unboxing and boxing can hit performance and since you want to perform something like collision detection, that might bring you low performance.

Is it mandatory for you to use structs? Due to the boxing changes done to the variables might not behave properly if you perform operations on the boxed objects stored, and as I mentioned too much boxing might bring you poor results.

In my opinion a class with MemberwiseClone would be better.

You can read this article on MSDN that details the pros and cons for both structs and classes, this might help you understand better when to use one or the other.

Each structure definition actually creates two kinds of thing in .NET: a heap object ("boxed") type, and a storage location ("unboxed") type. Interface-type storage locations hold heap-object references, so storing an unboxed structure to an interface-type variable will require that its contents be copied to an instance of the heap object type.

It is possible for a generic type parameter which is constrained to an interface to identify a structure storage-location type; one may then invoke the interface methods on the storage location in question without boxing. In some cases, this can offer some major performance advantages. Unfortunately, there's no way to tell the compiler "I don't want this thing boxed, and if I do anything which would require boxing I would rather have the compiler squawk than silently insert a boxing conversion". Consequently, extreme care is needed when using structures that implement interfaces; if one isn't willing to exercise such care, it is better to have all structures try to either:

  • behave like objects (in which case they should be be small and disallow any methods of mutation other than complete replacement) or

  • be nothing more than groups of variables stuck together with duct tape (ie a bunch of public fields).

Things that implement interfaces, outside of a few special cases like IEquatable<T> whose sole purpose centers around structures, should generally be classes.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM