Skip to content

Commit 520351c

Browse files
authored
Optimize reflection-based lookups using caches (#3179)
* Optimize lookups using reflection using caches, Cleanup code.
1 parent 25ba2a2 commit 520351c

File tree

23 files changed

+839
-736
lines changed

23 files changed

+839
-736
lines changed

.editorconfig

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ dotnet_sort_system_directives_first =
8484
dotnet_separate_import_directive_groups = false
8585
csharp_style_prefer_method_group_conversion = true:silent
8686
csharp_style_prefer_top_level_statements = true:silent
87+
csharp_using_directive_placement = outside_namespace:suggestion
8788

8889
#Formatting - spacing options
8990

@@ -160,7 +161,7 @@ csharp_style_prefer_extended_property_pattern =
160161
dotnet_style_namespace_match_folder = false:none
161162

162163
# Enable eventually
163-
dotnet_code_quality_unused_parameters = all:suggestion
164+
dotnet_code_quality_unused_parameters = all:silent
164165

165166
# Style - language keyword and framework type options
166167
#prefer the language keyword for local variables, method parameters, and class members,
@@ -389,7 +390,7 @@ dotnet_diagnostic.CA2014.severity =
389390
dotnet_diagnostic.IDE0058.severity = silent
390391

391392
# IDE0290: Use primary constructor
392-
csharp_style_prefer_primary_constructors = false:suggestion
393+
csharp_style_prefer_primary_constructors = false:silent
393394
# IDE0045: Convert to conditional expression
394395
dotnet_diagnostic.IDE0045.severity = none
395396
# IDE0065: Using directives must be placed inside a namespace declaration

Libraries/Opc.Ua.Server/NodeManager/MasterNodeManager.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2198,10 +2198,12 @@ public virtual void Call(
21982198
out CallMethodResultCollection results,
21992199
out DiagnosticInfoCollection diagnosticInfos)
22002200
{
2201+
#pragma warning disable CA2012 // Use ValueTasks correctly
22012202
(results, diagnosticInfos) = CallInternalAsync(
22022203
context,
22032204
methodsToCall,
22042205
sync: true).Result;
2206+
#pragma warning restore CA2012 // Use ValueTasks correctly
22052207
}
22062208

22072209
/// <summary>

Libraries/Opc.Ua.Server/Subscription/MonitoredItem/MonitoredItem.cs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -986,9 +986,7 @@ private EventFieldList GetEventFields(
986986
if (value != null)
987987
{
988988
// translate any localized text.
989-
var text = value as LocalizedText;
990-
991-
if (text != null)
989+
if (value is LocalizedText text)
992990
{
993991
value = m_server.ResourceManager.Translate(Session?.PreferredLocales, text);
994992
}

Stack/Opc.Ua.Core/Security/Constants/SecurityPolicies.cs

Lines changed: 66 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,13 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
1616
using System.Security.Cryptography;
1717
using System.Security.Cryptography.X509Certificates;
1818

19+
#if NET8_0_OR_GREATER
20+
using System.Collections.Frozen;
21+
#else
22+
using System.Linq;
23+
using System.Collections.ObjectModel;
24+
#endif
25+
1926
namespace Opc.Ua
2027
{
2128
/// <summary>
@@ -147,35 +154,30 @@ private static bool IsPlatformSupportedName(string name)
147154
}
148155

149156
/// <summary>
150-
/// Returns the uri associated with the display name.
157+
/// Returns the uri associated with the display name. This includes http and all
158+
/// other supported platform security policies.
151159
/// </summary>
152160
public static string GetUri(string displayName)
153161
{
154-
foreach (FieldInfo field in typeof(SecurityPolicies).GetFields(
155-
BindingFlags.Public | BindingFlags.Static))
162+
if (s_securityPolicyNameToUri.Value.TryGetValue(displayName, out string policyUri) &&
163+
IsPlatformSupportedName(displayName))
156164
{
157-
if (field.Name == displayName && IsPlatformSupportedName(field.Name))
158-
{
159-
return (string)field.GetValue(typeof(SecurityPolicies));
160-
}
165+
return policyUri;
161166
}
162167

163168
return null;
164169
}
165170

166171
/// <summary>
167-
/// Returns a display name for a security policy uri.
172+
/// Returns a display name for a security policy uri. This includes http and all
173+
/// other supported platform security policies.
168174
/// </summary>
169175
public static string GetDisplayName(string policyUri)
170176
{
171-
foreach (FieldInfo field in typeof(SecurityPolicies).GetFields(
172-
BindingFlags.Public | BindingFlags.Static))
177+
if (s_securityPolicyUriToName.Value.TryGetValue(policyUri, out string displayName) &&
178+
IsPlatformSupportedName(displayName))
173179
{
174-
if (policyUri == (string)field.GetValue(typeof(SecurityPolicies)) &&
175-
IsPlatformSupportedName(field.Name))
176-
{
177-
return field.Name;
178-
}
180+
return displayName;
179181
}
180182

181183
return null;
@@ -192,33 +194,21 @@ public static string GetDisplayName(string policyUri)
192194
/// </remarks>
193195
public static bool IsValidSecurityPolicyUri(string policyUri)
194196
{
195-
foreach (FieldInfo field in typeof(SecurityPolicies).GetFields(
196-
BindingFlags.Public | BindingFlags.Static))
197-
{
198-
if (policyUri == (string)field.GetValue(typeof(SecurityPolicies)))
199-
{
200-
return true;
201-
}
202-
}
203-
204-
return false;
197+
return s_securityPolicyUriToName.Value.ContainsKey(policyUri);
205198
}
206199

207200
/// <summary>
208-
/// Returns the display names for all security policy uris.
201+
/// Returns the display names for all security policy uris including https.
209202
/// </summary>
210203
public static string[] GetDisplayNames()
211204
{
212-
FieldInfo[] fields = typeof(SecurityPolicies).GetFields(
213-
BindingFlags.Public | BindingFlags.Static);
214-
var names = new List<string>();
205+
var names = new List<string>(s_securityPolicyUriToName.Value.Count);
215206

216-
// skip base Uri, ignore Https
217-
for (int ii = 1; ii < fields.Length - 1; ii++)
207+
foreach (string displayName in s_securityPolicyUriToName.Value.Values)
218208
{
219-
if (IsPlatformSupportedName(fields[ii].Name))
209+
if (IsPlatformSupportedName(displayName))
220210
{
221-
names.Add(fields[ii].Name);
211+
names.Add(displayName);
222212
}
223213
}
224214

@@ -620,5 +610,48 @@ public static bool Verify(
620610
securityPolicyUri);
621611
}
622612
}
613+
614+
/// <summary>
615+
/// Creates a dictionary of uris to name excluding base uri
616+
/// </summary>
617+
private static readonly Lazy<IReadOnlyDictionary<string, string>> s_securityPolicyUriToName =
618+
new(() =>
619+
{
620+
#if NET8_0_OR_GREATER
621+
return s_securityPolicyNameToUri.Value.ToFrozenDictionary(k => k.Value, k => k.Key);
622+
#else
623+
return new ReadOnlyDictionary<string, string>(
624+
s_securityPolicyNameToUri.Value.ToDictionary(k => k.Value, k => k.Key));
625+
#endif
626+
});
627+
628+
/// <summary>
629+
/// Creates a dictionary for names to uri excluding base uri
630+
/// </summary>
631+
private static readonly Lazy<IReadOnlyDictionary<string, string>> s_securityPolicyNameToUri =
632+
new(() =>
633+
{
634+
FieldInfo[] fields = typeof(SecurityPolicies).GetFields(
635+
BindingFlags.Public | BindingFlags.Static);
636+
637+
var keyValuePairs = new Dictionary<string, string>();
638+
foreach (FieldInfo field in fields)
639+
{
640+
string policyUri = (string)field.GetValue(typeof(SecurityPolicies));
641+
if (field.Name == nameof(BaseUri) ||
642+
field.Name == nameof(Https) ||
643+
!policyUri.StartsWith(BaseUri))
644+
{
645+
continue;
646+
}
647+
648+
keyValuePairs.Add(field.Name, policyUri);
649+
}
650+
#if NET8_0_OR_GREATER
651+
return keyValuePairs.ToFrozenDictionary();
652+
#else
653+
return new ReadOnlyDictionary<string, string>(keyValuePairs);
654+
#endif
655+
});
623656
}
624657
}

Stack/Opc.Ua.Core/Stack/Constants/DataTypes.Helpers.cs

Lines changed: 57 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,16 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
1111
*/
1212

1313
using System;
14+
using System.Collections.Generic;
1415
using System.Reflection;
1516

17+
#if NET8_0_OR_GREATER
18+
using System.Collections.Frozen;
19+
#else
20+
using System.Collections.ObjectModel;
21+
using System.Linq;
22+
#endif
23+
1624
namespace Opc.Ua
1725
{
1826
/// <summary>
@@ -21,57 +29,35 @@ namespace Opc.Ua
2129
public static partial class DataTypes
2230
{
2331
/// <summary>
24-
/// Returns the browse name for the attribute.
32+
/// Returns the browse names for all data types.
33+
/// </summary>
34+
public static IEnumerable<string> BrowseNames => s_dataTypeNameToId.Value.Keys;
35+
36+
/// <summary>
37+
/// Returns the browse name for the data type id.
2538
/// </summary>
2639
public static string GetBrowseName(int identifier)
2740
{
28-
foreach (FieldInfo field in typeof(DataTypes).GetFields(
29-
BindingFlags.Public | BindingFlags.Static))
30-
{
31-
if (identifier == (uint)field.GetValue(typeof(DataTypes)))
32-
{
33-
return field.Name;
34-
}
35-
}
36-
37-
return string.Empty;
41+
return s_dataTypeIdToName.Value.TryGetValue((uint)identifier, out string name)
42+
? name : string.Empty;
3843
}
3944

4045
/// <summary>
41-
/// Returns the browse names for all attributes.
46+
/// Returns the browse names for all data types.
4247
/// </summary>
48+
[Obsolete("Use BrowseNames property instead.")]
4349
public static string[] GetBrowseNames()
4450
{
45-
FieldInfo[] fields = typeof(DataTypes).GetFields(
46-
BindingFlags.Public | BindingFlags.Static);
47-
48-
int ii = 0;
49-
50-
string[] names = new string[fields.Length];
51-
52-
foreach (FieldInfo field in fields)
53-
{
54-
names[ii++] = field.Name;
55-
}
56-
57-
return names;
51+
return [.. BrowseNames];
5852
}
5953

6054
/// <summary>
61-
/// Returns the id for the attribute with the specified browse name.
55+
/// Returns the id for the data type with the specified browse name.
6256
/// </summary>
6357
public static uint GetIdentifier(string browseName)
6458
{
65-
foreach (FieldInfo field in typeof(DataTypes).GetFields(
66-
BindingFlags.Public | BindingFlags.Static))
67-
{
68-
if (field.Name == browseName)
69-
{
70-
return (uint)field.GetValue(typeof(DataTypes));
71-
}
72-
}
73-
74-
return 0;
59+
return s_dataTypeNameToId.Value.TryGetValue(browseName, out uint id)
60+
? id : 0;
7561
}
7662

7763
/// <summary>
@@ -137,5 +123,40 @@ public static Type GetSystemType(NodeId datatypeId, IEncodeableFactory factory)
137123
{
138124
return TypeInfo.GetSystemType(datatypeId, factory);
139125
}
126+
127+
/// <summary>
128+
/// Creates a dictionary of data type names to identifiers
129+
/// </summary>
130+
private static readonly Lazy<IReadOnlyDictionary<string, uint>> s_dataTypeNameToId =
131+
new(() =>
132+
{
133+
#if NET8_0_OR_GREATER
134+
return s_dataTypeIdToName.Value.ToFrozenDictionary(k => k.Value, k => k.Key);
135+
#else
136+
return new ReadOnlyDictionary<string, uint>(
137+
s_dataTypeIdToName.Value.ToDictionary(k => k.Value, k => k.Key));
138+
#endif
139+
});
140+
141+
/// <summary>
142+
/// Creates a dictionary of data type identifers to browse names.
143+
/// </summary>
144+
private static readonly Lazy<IReadOnlyDictionary<uint, string>> s_dataTypeIdToName =
145+
new(() =>
146+
{
147+
FieldInfo[] fields = typeof(DataTypes).GetFields(
148+
BindingFlags.Public | BindingFlags.Static);
149+
150+
var keyValuePairs = new Dictionary<uint, string>();
151+
foreach (FieldInfo field in fields)
152+
{
153+
keyValuePairs.Add((uint)field.GetValue(typeof(DataTypes)), field.Name);
154+
}
155+
#if NET8_0_OR_GREATER
156+
return keyValuePairs.ToFrozenDictionary();
157+
#else
158+
return new ReadOnlyDictionary<uint, string>(keyValuePairs);
159+
#endif
160+
});
140161
}
141162
}

0 commit comments

Comments
 (0)