Dynamics 365 Fin & Ops X++的If语句,可能跟你想的不太一样

Dynamics 365 Fin & Ops X++的If语句,可能跟你想的不太一样

IShirai_KurokoI

最近在做Azure.Storage.Files.Shares的时候,发现了一个奇怪的现象。

在C#中,判断文件夹是否存在的代码是这样的:

1
2
3
4
5
6
7
8
ShareClient shareClient = new ShareClient(connectionString, fileShareReference);
ShareDirectoryClient rootDir = shareClient.GetRootDirectoryClient();
ShareDirectoryClient fileDir = rootDir.GetSubdirectoryClient(_FilePath);

if (!fileDir.Exists())
{

}

在X++中,由于不支持default语法,所以我们要增加一个CancellationToken作为参数,代码如下:

1
2
3
4
5
6
7
8
ShareClient shareClient = new ShareClient(connectionString,fileShareReference);
ShareDirectoryClient rootDir = shareClient.GetRootDirectoryClient();
ShareDirectoryClient fileDir = rootDir.GetSubdirectoryClient(_FilePath);

if (!fileDir.Exists(CancellationToken::None))
{

}

但是,很奇怪的事,不论文件夹是否存在,都会进入if语句中。这是为什么呢?

如果我们反编译X++编译后的netmodule文件,会发现if语句的实现是这样的:

1
2
3
4
5
6
7
global::Azure.Storage.Files.Shares.ShareClient shareClient = new global::Azure.Storage.Files.Shares.ShareClient(this.connectionString, this.fileShareReference);
global::Azure.Storage.Files.Shares.ShareDirectoryClient rootDir = shareClient.GetRootDirectoryClient();
global::Azure.Storage.Files.Shares.ShareDirectoryClient fileDir = rootDir.GetSubdirectoryClient(_FilePath);
if (!global::Microsoft.Dynamics.Ax.Xpp.TrueFalseHelper.TrueFalse(fileDir.Exists(global::System.Threading.CancellationToken.None)))
{

}

我们会发现,X++的编译器在编译if语句的时候,会调用一个叫做TrueFalseHelper的方法,这个方法会将一个输入的Object转换为bool类型。

这个方法的实现是这样的(从框架代码中反编译):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Globalization;
using System.Reflection;
using System.Reflection.Emit;
using Microsoft.Dynamics.Ax.Xpp.AxShared;

namespace Microsoft.Dynamics.Ax.Xpp
{
public static class TrueFalseHelper
{
public static bool TrueFalse(object x)
{
if (x != null)
{
global::System.Type type = x.GetType();
if (!type.IsInterface)
{
if (x is global::System.Enum)
{
type = type.GetEnumUnderlyingType();
if (type == typeof(ulong))
{
x = (ulong)x;
}
else if (type == typeof(long))
{
x = (long)x;
}
else if (type == typeof(uint))
{
x = (uint)x;
}
else
{
x = global::System.Convert.ToInt32((global::System.Enum)x, global::System.Globalization.CultureInfo.InvariantCulture);
type = typeof(int);
}
}
global::Microsoft.Dynamics.Ax.Xpp.TrueFalseHelper.TrueFalseDelegate trueFalseDelegate = null;
if (!global::Microsoft.Dynamics.Ax.Xpp.TrueFalseHelper.trueFalseCaching.TryGetValue(type, out trueFalseDelegate))
{
global::System.Reflection.MethodInfo method = typeof(global::Microsoft.Dynamics.Ax.Xpp.TrueFalseHelper).GetMethod("TrueFalse", new global::System.Type[]
{
type
});
global::System.Reflection.Emit.DynamicMethod dynamicMethod = new global::System.Reflection.Emit.DynamicMethod("TrueFalseHelper::TrueFalse", typeof(bool), new global::System.Type[]
{
typeof(object)
});
global::System.Reflection.Emit.ILGenerator ilgenerator = dynamicMethod.GetILGenerator();
if (method.GetParameters()[0].ParameterType == typeof(object))
{
ilgenerator.Emit(global::System.Reflection.Emit.OpCodes.Ldc_I4_1);
}
else
{
ilgenerator.Emit(global::System.Reflection.Emit.OpCodes.Ldarg_0);
if (type.IsValueType)
{
ilgenerator.Emit(global::System.Reflection.Emit.OpCodes.Unbox_Any, type);
}
else
{
ilgenerator.Emit(global::System.Reflection.Emit.OpCodes.Castclass, type);
}
ilgenerator.Emit(global::System.Reflection.Emit.OpCodes.Call, method);
}
ilgenerator.Emit(global::System.Reflection.Emit.OpCodes.Ret);
trueFalseDelegate = (global::Microsoft.Dynamics.Ax.Xpp.TrueFalseHelper.TrueFalseDelegate)dynamicMethod.CreateDelegate(typeof(global::Microsoft.Dynamics.Ax.Xpp.TrueFalseHelper.TrueFalseDelegate));
global::Microsoft.Dynamics.Ax.Xpp.TrueFalseHelper.trueFalseCaching.TryAdd(type, trueFalseDelegate);
}
return trueFalseDelegate(x);
}
}
return x != null;
}

public static bool TrueFalse(object[] x)
{
return x != null && x.Length != 0 && x != global::Microsoft.Dynamics.Ax.Xpp.ContainerHelper.connull;
}

public static bool TrueFalse(string x)
{
return !string.IsNullOrEmpty(x);
}

public static bool TrueFalse(global::Microsoft.Dynamics.Ax.Xpp.Common x)
{
return global::Microsoft.Dynamics.Ax.Xpp.Common.IsNotNull(x);
}

public static bool TrueFalse(global::System.Guid x)
{
return !global::Microsoft.Dynamics.Ax.Xpp.PredefinedFunctions.NullGuid(x);
}

public static bool TrueFalse(global::Microsoft.Dynamics.Ax.Xpp.AxShared.utcdatetime x)
{
return !global::Microsoft.Dynamics.Ax.Xpp.PredefinedFunctions.NullDateTime(x);
}

public static bool TrueFalse(global::Microsoft.Dynamics.Ax.Xpp.AxShared.Date x)
{
return !global::Microsoft.Dynamics.Ax.Xpp.PredefinedFunctions.NullDate(x);
}

public static bool TrueFalse(long x)
{
return x != 0L;
}

public static bool TrueFalse(short x)
{
return x != 0;
}

public static bool TrueFalse(int x)
{
return x != 0;
}

public static bool TrueFalse(ushort x)
{
return x > 0;
}

public static bool TrueFalse(uint x)
{
return x > 0U;
}

public static bool TrueFalse(ulong x)
{
return x > 0UL;
}

public static bool TrueFalse(bool x)
{
return x;
}

public static bool TrueFalse(byte x)
{
return x > 0;
}

public static bool TrueFalse(sbyte x)
{
return x != 0;
}

public static bool TrueFalse(decimal x)
{
return x != 0m;
}

public static bool TrueFalse(float x)
{
return x != 0f;
}

public static bool TrueFalse(double x)
{
return x != 0.0;
}

public static bool TrueFalse(global::Microsoft.Dynamics.Ax.Xpp.XppObjectBase x)
{
return x != null;
}

public static bool TrueFalse<T>(global::System.Collections.Generic.Dictionary<int, T> x)
{
return x.Count != 0;
}

public static bool TrueFalse(global::Microsoft.Dynamics.Ax.Xpp.AxShared.IEdtArray x)
{
return x.Size != 0;
}

// Note: this type is marked as 'beforefieldinit'.
static TrueFalseHelper()
{
}

private static global::System.Collections.Concurrent.ConcurrentDictionary<global::System.Type, global::Microsoft.Dynamics.Ax.Xpp.TrueFalseHelper.TrueFalseDelegate> trueFalseCaching = new global::System.Collections.Concurrent.ConcurrentDictionary<global::System.Type, global::Microsoft.Dynamics.Ax.Xpp.TrueFalseHelper.TrueFalseDelegate>();

private delegate bool TrueFalseDelegate(object x);
}
}

我们再回到我们的问题上,为什么不论文件夹是否存在,都会进入if语句中?这就要回到Exists的返回值上了。
img.png

实际的返回类型是Azure.Response,而不是bool。所以,由于返回值永远是一个不为null的对象,所以TrueFalseHelper永远会返回true。而不是返回其中的bool值(在C#.net中会自动进行转换)。所以,我们需要手动取其中的bool值:

1
2
3
4
5
6
7
8
ShareClient shareClient = new ShareClient(connectionString,fileShareReference);
ShareDirectoryClient rootDir = shareClient.GetRootDirectoryClient();
ShareDirectoryClient fileDir = rootDir.GetSubdirectoryClient(_FilePath);

if (!fileDir.Exists(CancellationToken::None).Value)
{

}

这下就正常了。

ps: 我有一瞬间觉得我在写python XD

  • 标题: Dynamics 365 Fin & Ops X++的If语句,可能跟你想的不太一样
  • 作者: IShirai_KurokoI
  • 创建于 : 2025-01-15 17:59:00
  • 更新于 : 2025-01-15 18:39:08
  • 链接: https://ishiraikurokoi.top/2025-01-15-XPP-Weird-If/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。
评论
目录
Dynamics 365 Fin & Ops X++的If语句,可能跟你想的不太一样