C++/CLI: Boxing and Generic Lists

牧云@^-^@ 提交于 2019-12-11 01:53:03

问题


I am trying to create a generic list of references to PointF objects. (No, I am not looking to create a generic list of PointF objects.) However, the following line fails to compile:

Generic::List<PointF^> ^pointList; // Generates error C3225

On the other hand, creating an array of PointF references works without a problem as follows:

array<PointF^> ^points = gcnew array<PointF^>;

Here is a sample program:

using namespace System;
using namespace System::Drawing;
namespace Generic = System::Collections::Generic;

int main(array<System::String ^> ^args)
{

    array<PointF^> ^points = gcnew array<PointF^>{
        nullptr, PointF(0.0f, 0.0f), PointF(1.0f, 0.0f), nullptr
    };

    Generic::List<PointF^> ^pointList;
    Console::WriteLine(L"Hello World");
    return 0;
}

How do I create a generic list of PointF references? In other words, how do I create a generic list of boxed PointFs?


回答1:


It is a limit of the .Net generic, which only takes a CLI complient type such as a value type or a reference to a reference type. It does not take C++/CLI-specific types like stack semantics for ref types (which compiles into deterministic finalization) or, in you case, a reference to a boxed value type.

Array is native to CLI and does not have this restriction.




回答2:


PointF is not a class, it's a structure. You can't have references to a structure without boxing it inside an object.

You can either have a list of Object references and unbox the reference to PointF whenever you use it, or a list of a custom class that encapsulates a PointF value.

With implicit conversions to and from a PointF value you can make the boxing and unboxing transparent. I'm not sure how you write it in C++, but in C# it would look like this:

public class PointFObject {

   // encapsulated PointF structure
   private PointF _value;

   // constructor
   public PointFObject(PointF value) {
      _value = value;
   }

   // implicit conversion to a PointF value
   public static implicit operator PointF(PointFObject obj) {
      return obj._value;
   }

   // implicit conversion from a PointF value
   public static implicit operator PointFObject(PointF value) {
      return new PointFObject(value);
   }

}

Now you can create a list of PointFObject and access them as a list of PointF values:

List<PointFObject> pointList = new List<PointFObject>();
pointList.Add(new PointF(0f, 0f));
PointF p = pointList[0];



回答3:


Even though a storage location of type PointF and a heap object referred to by a PointF^ are different kinds of things, they are both described by the same Type object. The system generally decides which kind of thing a Type represents based upon how the type is used. If the PointF type is used to describe a storage location, that storage location will be allocated to hold a structure. The C++/CLI compiler can allow the declaration of a PointF^ variable, but the Framework has no concept of such a thing. Within C++/CLI code, the compiler can use a storage location of type Object to hold reference to a PointF heap object, or it can use an Object[] to hold a bunch of such references; if such locations are never exposed to the outside world and the compiler never stores anything but PointF references, the compiler can know the target of any non-null reference may be safely used as a PointF. The compiler can't expose such storage locations to outside code, however, because the type system offers no means of indicating that other code should be limited to storing PointF references.




回答4:


As others have mentioned, generic types only accept CLS-compliant type parameters. Since PointF^ is not CLS-compliant, List<PointF^> is invalid. array<> avoids this issue by being a template type, not a generic type.

However, there is a (fairly easy) workaround: create a List<Nullable<PointF>>. Your sample program then becomes:

using namespace System;
using namespace System::Drawing;
namespace Generic = System::Collections::Generic;

int main(array<System::String ^> ^args)
{

    array<Nullable<PointF>> ^points = gcnew array<Nullable<PointF>> {
        Nullable<PointF>(), Nullable<PointF>(PointF(0.0f, 0.0f)), Nullable<PointF>(PointF(1.0f, 0.0f)), Nullable<PointF>()
    };

    Generic::List<Nullable<PointF>> pointList(points);
    pointList.Add(PointF(2., 0.));
    Console::WriteLine(L"Hello World");
    return 0;
}


来源:https://stackoverflow.com/questions/1558620/c-cli-boxing-and-generic-lists

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!