diff --git a/lang/csharp/src/apache/main/GlobalSuppressions.cs b/lang/csharp/src/apache/main/GlobalSuppressions.cs index 64b5fb8b774..16e1576dbea 100644 --- a/lang/csharp/src/apache/main/GlobalSuppressions.cs +++ b/lang/csharp/src/apache/main/GlobalSuppressions.cs @@ -38,7 +38,7 @@ [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1000:Do not declare static members on generic types", Justification = "Maintain public API", Scope = "member", Target = "~M:Avro.File.DataFileWriter`1.OpenAppendWriter(Avro.Generic.DatumWriter{`0},System.String)~Avro.File.IFileWriter{`0}")] [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1000:Do not declare static members on generic types", Justification = "Maintain public API", Scope = "member", Target = "~M:Avro.File.DataFileWriter`1.OpenAppendWriter(Avro.Generic.DatumWriter{`0},System.IO.Stream,System.IO.Stream)~Avro.File.IFileWriter{`0}")] [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "Purposely catch any exception", Scope = "member", Target = "~M:Avro.File.DataFileReader`1.Sync(System.Int64)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "Purposely catch any exception", Scope = "member", Target = "~M:Avro.Specific.ObjectCreator.FindType(System.String)~System.Type")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "Purposely catch any exception", Scope = "member", Target = "~M:Avro.Specific.ObjectCreator.FindTypeUncached(System.String)~System.Type")] [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "Purposely catch any exception", Scope = "member", Target = "~M:Avro.Specific.ObjectCreator.GetType(Avro.Schema)~System.Type")] [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1051:Do not declare visible instance fields", Justification = "Maintain public API", Scope = "member", Target = "~F:Avro.CodeGen.namespaceLookup")] [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1051:Do not declare visible instance fields", Justification = "Maintain public API", Scope = "member", Target = "~F:Avro.Generic.GenericFixed.value")] diff --git a/lang/csharp/src/apache/main/Specific/ObjectCreator.cs b/lang/csharp/src/apache/main/Specific/ObjectCreator.cs index 073b107958a..f1831e21cd6 100644 --- a/lang/csharp/src/apache/main/Specific/ObjectCreator.cs +++ b/lang/csharp/src/apache/main/Specific/ObjectCreator.cs @@ -58,6 +58,10 @@ public sealed class ObjectCreator private readonly Assembly entryAssembly; private readonly bool diffAssembly; + // Cached value factory for typeCacheByName.GetOrAdd. Stored once so that + // FindType does not allocate a new closure on every call (AVRO-3893). + private readonly Func findTypeFactory; + /// /// Initializes a new instance of the class. /// @@ -69,6 +73,8 @@ public ObjectCreator() // entryAssembly returns null when running from NUnit diffAssembly = entryAssembly != null && execAssembly != entryAssembly; + + findTypeFactory = FindTypeUncached; } /// @@ -81,62 +87,70 @@ public ObjectCreator() /// private Type FindType(string name) { - return typeCacheByName.GetOrAdd(name, (_) => - { - Type type = null; + return typeCacheByName.GetOrAdd(name, findTypeFactory); + } - if (TryGetIListItemTypeName(name, out var itemTypeName)) - { - return GenericIListType.MakeGenericType(FindType(itemTypeName)); - } + /// + /// Value factory used by . Kept as a separate method (referenced + /// through the cached findTypeFactory delegate) so the lookup does not capture a + /// per-call closure. The argument is the cache key supplied by + /// . + /// + private Type FindTypeUncached(string name) + { + Type type = null; - if (TryGetNullableItemTypeName(name, out itemTypeName)) - { - return GenericNullableType.MakeGenericType(FindType(itemTypeName)); - } + if (TryGetIListItemTypeName(name, out var itemTypeName)) + { + return GenericIListType.MakeGenericType(FindType(itemTypeName)); + } - // if entry assembly different from current assembly, try entry assembly first - if (diffAssembly) - { - type = entryAssembly.GetType(name); - } + if (TryGetNullableItemTypeName(name, out itemTypeName)) + { + return GenericNullableType.MakeGenericType(FindType(itemTypeName)); + } - // try current assembly and mscorlib - if (type == null) - { - type = Type.GetType(name); - } + // if entry assembly different from current assembly, try entry assembly first + if (diffAssembly) + { + type = entryAssembly.GetType(name); + } + + // try current assembly and mscorlib + if (type == null) + { + type = Type.GetType(name); + } - // type is still not found, need to loop through all loaded assemblies - if (type == null) + // type is still not found, need to loop through all loaded assemblies + if (type == null) + { + foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies()) { - foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies()) + // Loading all types from all assemblies could fail for a variety of + // non -fatal reasons. If we fail to load types from an assembly, continue. + try { - // Loading all types from all assemblies could fail for a variety of - // non -fatal reasons. If we fail to load types from an assembly, continue. - try + // Change the search to look for Types by both NAME and FULLNAME + foreach (Type t in assembly.GetTypes()) { - // Change the search to look for Types by both NAME and FULLNAME - foreach (Type t in assembly.GetTypes()) + if (name == t.Name || name == t.FullName || CodeGenUtil.Instance.UnMangle(name) == t.FullName) { - if (name == t.Name || name == t.FullName || CodeGenUtil.Instance.UnMangle(name) == t.FullName) - { - type = t; - break; - } + type = t; + break; } } - catch - { - continue; - } + } + catch + { + continue; } } + } - return type - ?? throw new AvroException($"Unable to find type '{name}' in all loaded " + - $"assemblies"); - }); + return type + ?? throw new AvroException($"Unable to find type '{name}' in all loaded " + + $"assemblies"); } private bool TryGetIListItemTypeName(string name, out string itemTypeName)