将Delphi集从C#传递给外部Delphi函数

Mar*_*Pim 3 c# delphi set marshalling

我试图从C#调用外部Delphi函数,它将Delphi设置作为参数:

德尔福代码

type
  tStatus = (sIn, sOut, sAbsent, sSick);
  tStatusSet = set of tStatus;

function LoadEmployees(tStatusSet aStatusSet): tEmpList;
Run Code Online (Sandbox Code Playgroud)

我需要马歇尔枚举值(即从元件的C#阵列tStatus)成Delphi将读取为一个格式tStatusSet类型:

C#代码

tStatusSet lStatusSet = ConvertToDelphiSet(sIn, sOut);

tEmpList lEmpList = LoadEmployees(aStatusSet);
Run Code Online (Sandbox Code Playgroud)

ConvertToDelphiSet理想情况下应该是一个通用的解决方案,能够应对任何枚举.我们将其定义为:

int ConvertToDelphiSet<T>(params T[] aArgs) {
  int lResult = 0;

  foreach (T lItem in aArgs)
  {
    int lValue = lItem.ToInt32();
    lValue = (int)Math.Pow(2, lValue);

    lResult |= lValue;
  }
Run Code Online (Sandbox Code Playgroud)

但这并没有返回正确的值(例如,传递它所有四个tStatus值导致Delphi只看到集合中的第三个值).

是否有关于Delphi内部代表集合的文档?它是所有值的简单位域吗?是否有更强大的方法来实现这一目标?这是未来的证据,还是我依赖​​于可能发生变化的无证内部功能?

Del*_*ics 10

我相信这是一个有点领域,但可能不是你所谓的"简单".

Delphi集最多可包含256个元素,或者值最多为255的元素,这可能不一定是相同的,因为枚举成员可以被赋予特定值并且不一定是连续的,例如:

  TEnum = (a, b, c=255);
  TSet  = set of TEnum;
Run Code Online (Sandbox Code Playgroud)

结果在TSet中可能的最大大小为256位,即使它只有3个可能的成员.(并注意成员"a"的值为0,而不是1!)

如果在set类型或该类型的变量上使用sizeof()函数,则可以看到这一点,这将指示该类型或变量占用的存储字节数.

  TEnum = (a, b, c=255);
  TSet  = set of TEnum;

  >>> sizeof(TSet) = 32


  TEnum = (a, b, c);
  TSet  = set of TEnum;

  >>> sizeof(TSet) = 1
Run Code Online (Sandbox Code Playgroud)

您设计的依赖于Delphi集类型的内部存储的任何机制都将是脆弱的,并且要求您在C#和Delphi中定义的枚举类型匹配但不能轻易/可靠地识别为不同步.

如果这是一个实际问题,那么我建议您将值作为枚举成员名称数组传递,并使用Delphi中的RTTI通过将枚举名称转换为相应的枚举值并添加它们来重建Delphi端的集合根据需要添加到您的集合中(如果指定了无效的枚举成员名称,则返回-1作为GetEnumValue的枚举值):

  enumValue := TEnum( GetEnumValue(TypeInfo(TEnum), sEnumMemberName) );
  if Ord(enumValue) = -1 then
     raise Exception.Create('Invalid enum value' + sEnumMemberName);

  Include(setVar, enumValue);
Run Code Online (Sandbox Code Playgroud)

GetEnumValue()和相应的GetEnumName()函数是TypInfo单元的一部分.

通过这种方式,您将能够检测并处理C#代码指定了Delphi代码无法识别的枚举值(按名称)(当然反之亦然)的情况,其方式可能不那么可靠在对任意n字节存储块中的匿名位进行假设时.

您当然必须确保您的C#和Delphi代码都使用相同或容易映射的枚举成员名称.

折衷方案可能是将值作为简单的字节数组传递=枚举的每个成员都有一个基础字节大小的序数值,因此只要C#中的枚举成员与Delphi中的枚举成员具有相同的基本序号值您可以根据需要简单地投射它们,例如,您应该能够很容易地适应的非常粗糙的伪代码:

给定:

     enumValues: array of Byte;
     setVar: TSet;  // set of TEnum

  for i := 0 to Length(enumValues) do
    Include(setVar, TEnum(enumValues[i]));
Run Code Online (Sandbox Code Playgroud)