ValueType to IntPtr для использования в течение времени жизни приложения - PullRequest
1 голос
/ 04 июня 2011

Допустим, у нас есть собственная библиотека, которая работает с такими данными:

 double *pointer = &globalValue; 
 SetAddress(pointer);

 //Then we can change value and write it to disk
 globalValue = 5.0;

 FlushValues(); // this function writes all values 
                // registered by SetAddress(..) functions

 ... //Then we change our value (or values)
 FlushValues(); //and do write any time we need

Теперь мне нужно подключить ее к C #.Я хотел бы избежать использования небезопасных ... но ... я не знаю =)

Так что на стороне C # у нас будет несколько классов, поля которых мы напишем.И мы можем писать так:

 public class Data
 {
     [Serializable] <-- somehow we mark what we are going to write
     double myValue;

     ...

     [Serializable]
     double myValueN;
 }

 public class LibraryInterop
 {
      IntPtr[] pointers; //Pointers that will go to SetAddressMethod
      ...

      public void RegisterObject(Data data)
      {
          ... //With reflection we look for [Serializable] values
              //create a pointer for each and some kind of BindingInfo 
              //that knows connection of pointers[i] to data.myValueN

          //Then add this pointers to C++ library
          foreach(IntPtr pointer in pointers) { SetAddress(pointer);}
      }

      public void Flush() 
      {
          //Loop through all added pointers
          for(int i=0; i<pointers.Length; i++)
          {
              double value = ... //With reflections and BindingInfo we find data.myValueN
                                 // that corresponds to pointers[i]

              // then we write this value to memory of IntPtr
              // we have to brake value to bytes and write it one by one to memory
              // we could use BitConverter.DoubleToInt64Bits() but double - is just 
              // an example, so in general case we will use GetBytes
              int offset = 0;
              foreach(byte b in BitConverter.GetBytes(value))
              {
                  Marshal.WriteByte(pointer[i],offset,byte);
                  offset++;
              }
          }

          //Save values of IntPtr to disk
          FlushValues();
      }
  }

Тогда код пользователя будет выглядеть так:

  var library = new LibraryInterop();
  var data1 = new Data();
  var data2 = new AnotherData();
  library.RegisterObject(data1);
  library.RegisterObject(data2);

  ...//change data
  library.Flush();
  ...//change data
  library.Flush();
  ...//change data
  library.Flush();

Так что в C ++ мы имеем очень аккуратную структуру.У нас есть указатели, мы заполняем данные за этими указателями и в FlushValues ​​все эти значения записываются.

Часть C # не может иметь SetAddress (ref double value).Поскольку адрес может измениться, мы должны закрепить указатели - использовать unsafe + fixed или IntPtr, и у нас будет SO много головной боли.

С другой стороны, мы можем иметь «управляемые указатели», помещая в коробку | unboxing data.myValue в Object.Поэтому, если бы можно было как-то связать этот ValueType data.myValue с IntPtr без этого копирования и отражения - это было бы намного лучше.

Возможно ли сделать это взаимодействие и иметь менее уродливую и медленную часть C #, чем та, которую я перечислил здесь?

1 Ответ

2 голосов
/ 04 июня 2011

(Есть некоторые существенные недостатки этого ...)

Вы можете использовать GCHandle . Alloc (data1, GCHandleType.Pinned ), чтобы «закрепить» объект, а затем получить IntPtr из GCHandle.AddrOfPinnedObject . Если вы сделаете это, вы сможете передать этот IntPtr в ваш собственный код, который должен работать как положено.

Однако это вызовет много проблем с точки зрения подрыва эффективности сборщика мусора. Если вы решите сделать что-то подобное, я бы порекомендовал выделить все объекты на ранней стадии вашей программы (возможно, сразу после вызова GC.Collect(), что я обычно не рекомендую) и оставить их в покое. на всю жизнь вашей программы. Это (немного) уменьшит проблемы с GC, так как это приведет к их раннему ранжированию в «наилучшем» месте и оставит их там, где они будут затронуты только в коллекциях Gen2.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...