-
Notifications
You must be signed in to change notification settings - Fork 5.2k
Description
Background and Motivation
While working on the implementation of TypeName API (#97566), it turned out that it's impossible to set AssemblyName.CultureName without allocating a CultrueInfo instance (using public NS2.0 compatibile APIs).
runtime/src/libraries/System.Private.CoreLib/src/System/Reflection/AssemblyName.cs
Line 83 in 0764864
| set => _cultureInfo = (value == null) ? null : new CultureInfo(value); |
The type name parser needs to support parsing from untrusted input and such non-predefined culture name may be used as a vector of attack, so the existing AssemblyName is not suitable for our needs.
Since AssemblyName suffers from some other design issues (mutability, a LOT of obsolete properties), I would like to introduce a new, immutable, lightweight version of AssemblyName type.
Proposed API
It's a concept and I need the help of API review board to choose the right name. Some of my ideas: ReadOnlyAssemblyName, AssemblyId, LibraryName.
namespace System.Reflection.Metadata;
public sealed class AssemblyNameInfo : IEquatable<AssemblyNameInfo>
{
public AssemblyNameInfo(string name, Version? version = null, string? cultureName = null, AssemblyNameFlags flags = AssemblyNameFlags.None,
Collections.Immutable.ImmutableArray<byte> publicKeyOrToken = default);
public string Name { get; }
public string FullName { get; }
public Version? Version { get; }
public string? CultureName { get; }
public AssemblyNameFlags Flags { get; }
public ImmutableArray<byte> PublicKeyOrToken { get; }
public AssemblyName ToAssemblyName();
public static AssemblyNameInfo Parse(ReadOnlySpan<char> assemblyName);
public static bool TryParse(ReadOnlySpan<char> assemblyName, [NotNullWhenAttribute(true)] out AssemblyNameInfo? result);
}The approved TypeName API needs following changes:
public sealed partial class TypeName : IEquatable<TypeName>
{
- public string AssemblySimpleName { get; }
- public System.Reflection.AssemblyName? GetAssemblyName();
+ public AssemblyNameInfo? AssemblyName { get; }
}Usage Examples
Building high-level, opinionated parser on top of the low-level API:
AssemblyName GetAssemblyName(string unsafeInput)
{
if (!AssemblyNameInfo.TryParse(unsafeInput, out AssemblyNameInfo info))
{
throw new ArgumentException("Invalid name!");
}
if (info.Name.AsSpan().IndexOfAny(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar) >= 0)
{
throw new InvalidOperationException("AssemblyName must not contain path characters.");
}
// the following line throws CultureNotFoundException for non-predefined cultures
_ = CultureInfo.GetCultureInfo(info.CultureName, predefinedOnly: true);
return info.ToAssemblyName();
}Alternative Designs
The proposed design has one property PublickKeyOrToken that can express the public key or its token. It's a design choice inherited from AssemblyName that uses AssemblyNameFlags.PublicKey flag to express what given byte array is.
We could have two dedicated properties and ctor arguments to express that: PublicKey and PublicKeyToken.
Risks
Providing low quality design may require introducing another AssemblyName-like type in the future.