设置“可为空”是指将其设置为null。 .NET对象的属性 [英] Setting a "nullable" property on a .NET object

查看:334
本文介绍了设置“可为空”是指将其设置为null。 .NET对象的属性的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一些.NET互操作代码,可以在其中加载对象并读取属性,但是在设置对象的属性时遇到了麻烦。以下是Delphi代码的相关部分:

 使用
mscorlib_TLB,Winapi.ActiveX;

type
//代码的无关部分
TDotNetObject = class(TObject)
private
FTarget:OleVariant;
FType:_Type;
public
过程SetProperty(const APropertyName:string; const AValue:OleVariant; const AIndex:Integer = -1);
结尾;

函数VariantToPSafeArray(const AValue:Variant):PSafeArray;
开始
结果:= PSafeArray(VarArrayAsPSafeArray(AValue));
结尾;

过程TDotNetObject.SetProperty(const APropertyName:string; const AValue:OleVariant; const AIndex:Integer = -1);
var
LPropertyInfo:_PropertyInfo;
LIndex:PSafeArray;
如果AIndex> = 0则以
开始,然后
LIndex:= VariantToPSafeArray(VarArrayOf([AIndex]))
否则
LIndex:= nil;
LPropertyInfo:= FType.GetProperty(APropertyName,BindingFlags_Instance或BindingFlags_Public或BindingFlags_NonPublic);如果LPropertyInfo<>为
nil然后
LPropertyInfo.SetValue(FTarget,AValue,LIndex);
结尾;

过程UpdateDefectStatus(const ADefectID,AStatus:Integer);
var
LObject:TDotNetObject;
begin
// **获取省略的对象的代码***
LObject.SetProperty('Status',AStatus);
结尾;

mscorlib_TLB单元来自JEDI项目中的JCL,在这里:



https:// github.com/project-jedi/jcl/blob/master/jcl/source/windows/mscorlib_TLB.pas



LPropertyInfo.SetValue引发错误在TDotNetObject.SetProperty中被调用:无法转换为类型'System.Nullable`1 [MTData.Transport.Tracking.DefectReporting.DefectStatus]。

C#对象的DefectStatus属性声明为:

 公共缺陷状态?状态

(即可以为空)



C#中的Status属性类型声明为:

 公共枚举DefectStatus 
{
///<摘要>
///已报告缺陷。
///< / summary>
已报告,
///< summary>
///评估缺陷。
///< / summary>
已评估,
///< summary>
///工作单上有缺陷。
///< / summary>
OnWorkOrder,
///< summary>
///缺陷已关闭。
///< / summary>
已关闭
}

我找到了使用以下方法处理这种情况的解决方案C#在这里:



https://stackoverflow.com/a/13270302/ 3164070



但是我对如何在Delphi中执行相同操作有些迷惑。有想法吗?



编辑



鉴于奥利维尔的答案,我试图写一些Delphi代码可以完成等效操作,如下所示:

  procedure InvokeToObject; 
var
LType,LDefectStatusType:_Type;
LInvokeFlags:TOleEnum;
LArgs:PSafeArray;
LValue:整数;
L结果:OleVariant;
LRes:HRESULT;
LResHex:字符串;
开始
LType:= MTDataClr.GetCoreType('System.Enum');
LDefectStatusType:= MTDataClr.GetType(’MTData.Transport.Tracking.DefectReporting.DefectStatus’);
LInvokeFlags:= BindingFlags_InvokeMethod或BindingFlags_Static;
LValue:= 1;
LArgs:= VariantToPSafeArray(VarArrayOf([[LDefectStatusType,LValue])));
LRes:= LType.InvokeMember_2( ToObject,LInvokeFlags,nil,Null,LAgs,nil,LResult);
LResHex:= IntToHex(LRes);
结尾;

这段代码的目标只是调用Enum类型的ToObject方法。成功获取LType和LDefectStatusType,但是对InvokeMember_2的调用未成功,返回码为:0x80131512,这显然是Missing Member异常。对我做错的任何想法吗?

解决方案

问题是枚举不是int,这意味着 SetValue()需要执行两次转换( Int32 DefectStatus DefectStatus DefectStatus?),它不能(只能执行一个)。



下面是一个C#代码,可再现您要执行的操作:

 使用系统; 
使用System.Reflection;

公共枚举DefectStatus
{
已报告,
已评估,
OnWorkOrder,
已关闭
}

公共类Defect
{
public DefectStatus?状态{get; set;}
}

公共类测试
{
public static void Main()
{
Defect def = new Defect() ;

PropertyInfo pi = typeof(Defect).GetProperty( Status);

//抛出ArgumentException
// pi.SetValue(def,1,null);

//通过其数字值检索被评估的枚举
对象评估= Enum.ToObject(typeof(DefectStatus),1);

//可以正常使用
pi.SetValue(def,评估为null);

Console.WriteLine(def.Status);
}
}

所以您需要在Delphi中检索枚举。为此,您将需要使用API​​来访问 Enum 类型并在其上调用 ToObject


I have some .NET interop code where I've managed to load objects and read properties, however I am having trouble with setting a property on an object. Here's the relevant parts of the Delphi code:

uses
  mscorlib_TLB, Winapi.ActiveX;

type
  // Irrelevant parts of the code omitted
  TDotNetObject = class(TObject)
  private
    FTarget: OleVariant;
    FType: _Type;
  public
    procedure SetProperty(const APropertyName: string; const AValue: OleVariant; const AIndex: Integer = -1);
  end;

function VariantToPSafeArray(const AValue: Variant): PSafeArray;
begin
  Result := PSafeArray(VarArrayAsPSafeArray(AValue));
end;

procedure TDotNetObject.SetProperty(const APropertyName: string; const AValue: OleVariant; const AIndex: Integer = -1);
var
  LPropertyInfo: _PropertyInfo;
  LIndex: PSafeArray;
begin
  if AIndex >= 0 then
    LIndex := VariantToPSafeArray(VarArrayOf([AIndex]))
  else
    LIndex := nil;
  LPropertyInfo := FType.GetProperty(APropertyName, BindingFlags_Instance or BindingFlags_Public or BindingFlags_NonPublic);
  if LPropertyInfo <> nil then
    LPropertyInfo.SetValue(FTarget, AValue, LIndex);
end;

procedure UpdateDefectStatus(const ADefectID, AStatus: Integer);
var
  LObject: TDotNetObject;
begin
  // ** Code to obtain the object omitted ***
  LObject.SetProperty('Status', AStatus);
end;

The mscorlib_TLB unit comes from JCL in Project JEDI, here:

https://github.com/project-jedi/jcl/blob/master/jcl/source/windows/mscorlib_TLB.pas

An error is thrown when LPropertyInfo.SetValue is called in TDotNetObject.SetProperty:

Project TestProject.exe raised exception class EOleException with message 'Object of type 'System.Int32' cannot be converted to type 'System.Nullable`1[MTData.Transport.Tracking.DefectReporting.DefectStatus]''.

The DefectStatus property on the C# object is declared as:

public DefectStatus? Status

(i.e. it's nullable)

The Status property type in C# is declared:

public enum DefectStatus
{
    /// <summary>
    /// Defect Reported.
    /// </summary>
    Reported,
    /// <summary>
    /// Defect assessed.
    /// </summary>
    Assessed,
    /// <summary>
    /// Defect on work order.
    /// </summary>
    OnWorkOrder,
    /// <summary>
    /// Defect closed.
    /// </summary>
    Closed
}

I found a solution for how to handle this situation using C# here:

https://stackoverflow.com/a/13270302/3164070

However I'm a bit lost as to how to do the same in Delphi. Any ideas?

EDIT

Given Olivier's answer, I have attempted to write some Delphi code to do the equivalent, which is as follows:

procedure InvokeToObject;
var
  LType, LDefectStatusType: _Type;
  LInvokeFlags: TOleEnum;
  LArgs: PSafeArray;
  LValue: Integer;
  LResult: OleVariant;
  LRes: HRESULT;
  LResHex: string;
begin
  LType := MTDataClr.GetCoreType('System.Enum');
  LDefectStatusType := MTDataClr.GetType('MTData.Transport.Tracking.DefectReporting.DefectStatus');
  LInvokeFlags := BindingFlags_InvokeMethod or BindingFlags_Static;
  LValue := 1;
  LArgs := VariantToPSafeArray(VarArrayOf([LDefectStatusType, LValue]));
  LRes := LType.InvokeMember_2('ToObject', LInvokeFlags, nil, Null, LArgs, nil, LResult);
  LResHex := IntToHex(LRes);
end;

The goal with this piece of code is just to invoke the ToObject method of the Enum type. Obtaining LType and LDefectStatusType succeeds, however the call to InvokeMember_2 does not, with a return code of: 0x80131512, which apparently is a Missing Member exception. Any ideas on what I'm doing wrong?

解决方案

The issue is that an enum is not an int, which means SetValue() would need to perform a double conversion (Int32 to DefectStatus and DefectStatus to DefectStatus?), which it cannot (it can only perform one).

Here's a C# code that reproduces what you're trying to do:

using System;
using System.Reflection;

public enum DefectStatus
{
    Reported,
    Assessed,
    OnWorkOrder,
    Closed
}

public class Defect
{
    public DefectStatus? Status {get; set;}
}

public class Test
{
    public static void Main()
    {
        Defect def = new Defect();

        PropertyInfo pi = typeof(Defect).GetProperty("Status");

        // This throws an ArgumentException
//      pi.SetValue(def, 1, null);

        // Retrieve the Assessed enum via its numeric value
        object assessed = Enum.ToObject(typeof(DefectStatus), 1);

        // This works as expected
        pi.SetValue(def, assessed, null);

        Console.WriteLine(def.Status);
    }
}

So you need to retrieve the enum in Delphi. For that you will need to play with the API to access the Enum type and call ToObject on it.

这篇关于设置“可为空”是指将其设置为null。 .NET对象的属性的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆