diff --git a/src/libraries/Common/src/System/SR.cs b/src/libraries/Common/src/System/SR.cs index eabb2d57560454..9bc27cb42f7d08 100644 --- a/src/libraries/Common/src/System/SR.cs +++ b/src/libraries/Common/src/System/SR.cs @@ -15,7 +15,17 @@ internal static partial class SR // The trimming tools are also capable of replacing the value of this method when the application is being trimmed. internal static bool UsingResourceKeys() => s_usingResourceKeys; - internal static string GetResourceString(string resourceKey) + // We can optimize out the resource string blob if we can see all accesses to it happening + // through the generated SR.XXX properties. + // If a call to GetResourceString is left, the optimization gets defeated and we need to keep + // the whole resource blob. It's important to keep this private. CoreCLR's CoreLib gets a free + // pass because the VM needs to be able to call into this, but that's a known set of constants. +#if CORECLR || LEGACY_GETRESOURCESTRING_USER + internal +#else + private +#endif + static string GetResourceString(string resourceKey) { if (UsingResourceKeys()) { @@ -37,7 +47,12 @@ internal static string GetResourceString(string resourceKey) return resourceString!; // only null if missing resources } - internal static string GetResourceString(string resourceKey, string defaultString) +#if LEGACY_GETRESOURCESTRING_USER + internal +#else + private +#endif + static string GetResourceString(string resourceKey, string defaultString) { string resourceString = GetResourceString(resourceKey); diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft.CSharp.csproj b/src/libraries/Microsoft.CSharp/src/Microsoft.CSharp.csproj index ccee74eabc4ac9..3be4e969da5a7d 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft.CSharp.csproj +++ b/src/libraries/Microsoft.CSharp/src/Microsoft.CSharp.csproj @@ -9,6 +9,7 @@ true $(DefineConstants);ENABLECOMBINDER true + $(DefineConstants);LEGACY_GETRESOURCESTRING_USER false diff --git a/src/libraries/Microsoft.Extensions.Primitives/src/ThrowHelper.cs b/src/libraries/Microsoft.Extensions.Primitives/src/ThrowHelper.cs index 3d71f00e03c65b..06fa14a31cf0c0 100644 --- a/src/libraries/Microsoft.Extensions.Primitives/src/ThrowHelper.cs +++ b/src/libraries/Microsoft.Extensions.Primitives/src/ThrowHelper.cs @@ -58,7 +58,20 @@ internal static ArgumentException GetArgumentException(ExceptionResource resourc private static string GetResourceText(ExceptionResource resource) { - return SR.GetResourceString(GetResourceName(resource)); + Debug.Assert(Enum.IsDefined(typeof(ExceptionResource), resource), + "The enum value is not defined, please check the ExceptionResource Enum."); + + switch (resource) + { + case ExceptionResource.Argument_InvalidOffsetLength: return SR.Argument_InvalidOffsetLength; + case ExceptionResource.Argument_InvalidOffsetLengthStringSegment: return SR.Argument_InvalidOffsetLengthStringSegment; + case ExceptionResource.Capacity_CannotChangeAfterWriteStarted: return SR.Capacity_CannotChangeAfterWriteStarted; + case ExceptionResource.Capacity_NotEnough: return SR.Capacity_NotEnough; + case ExceptionResource.Capacity_NotUsedEntirely: return SR.Capacity_NotUsedEntirely; + default: + Debug.Fail($"Unexpected resource {resource}"); + return ""; + } } private static string GetArgumentName(ExceptionArgument argument) @@ -68,14 +81,6 @@ private static string GetArgumentName(ExceptionArgument argument) return argument.ToString(); } - - private static string GetResourceName(ExceptionResource resource) - { - Debug.Assert(Enum.IsDefined(typeof(ExceptionResource), resource), - "The enum value is not defined, please check the ExceptionResource Enum."); - - return resource.ToString(); - } } internal enum ExceptionArgument diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ArrayConverter.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ArrayConverter.cs index 2cabc7eaf1e1f1..cf9130f194303f 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ArrayConverter.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ArrayConverter.cs @@ -19,7 +19,7 @@ public class ArrayConverter : CollectionConverter { if (destinationType == typeof(string) && value is Array) { - return SR.Format(SR.GetResourceString(nameof(SR.Array), "{0} Array"), value.GetType().Name); + return SR.Format(SR.UsingResourceKeys() ? "{0} Array" : SR.Array, value.GetType().Name); } return base.ConvertTo(context, culture, value, destinationType); diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/CollectionConverter.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/CollectionConverter.cs index 4f0f1dcc5b9e61..3a0b59eabbaf8b 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/CollectionConverter.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/CollectionConverter.cs @@ -20,7 +20,7 @@ public class CollectionConverter : TypeConverter { if (destinationType == typeof(string) && value is ICollection) { - return SR.GetResourceString(nameof(SR.Collection), "(Collection)"); + return SR.UsingResourceKeys() ? "(Collection)" : SR.Collection; } return base.ConvertTo(context, culture, value, destinationType); diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/CultureInfoConverter.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/CultureInfoConverter.cs index fc9d63999632c3..014a3e980dded7 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/CultureInfoConverter.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/CultureInfoConverter.cs @@ -22,7 +22,7 @@ public class CultureInfoConverter : TypeConverter /// /// Retrieves the "default" name for our culture. /// - private static string DefaultCultureString => SR.GetResourceString(nameof(SR.CultureInfoConverterDefaultCultureString), "(Default)"); + private static string DefaultCultureString => SR.UsingResourceKeys() ? "(Default)" : SR.CultureInfoConverterDefaultCultureString; private const string DefaultInvariantCultureString = "(Default)"; diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/DesignerOptionService.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/DesignerOptionService.cs index 3188cf2eb76cbf..2185bc5d1a7cbf 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/DesignerOptionService.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/DesignerOptionService.cs @@ -461,7 +461,7 @@ public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContex { if (destinationType == typeof(string)) { - return SR.GetResourceString(nameof(SR.CollectionConverterText), "(Collection)"); + return SR.UsingResourceKeys() ? "(Collection)" : SR.CollectionConverterText; } return base.ConvertTo(cxt, culture, value, destinationType); } diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ExtendedPropertyDescriptor.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ExtendedPropertyDescriptor.cs index 62e6476237e5f6..b0235f0aa2d0d7 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ExtendedPropertyDescriptor.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ExtendedPropertyDescriptor.cs @@ -99,7 +99,7 @@ public override string DisplayName string? providerName = site?.Name; if (providerName != null && providerName.Length > 0) { - name = SR.Format(SR.GetResourceString(nameof(SR.MetaExtenderName), "{0} on {1}"), name, providerName); + name = SR.Format(SR.UsingResourceKeys() ? "{0} on {1}" : SR.MetaExtenderName, name, providerName); } } return name; diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/InstanceCreationEditor.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/InstanceCreationEditor.cs index 5f0ab1fe809e8c..7591dad5828c5e 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/InstanceCreationEditor.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/InstanceCreationEditor.cs @@ -11,7 +11,7 @@ namespace System.ComponentModel /// public abstract class InstanceCreationEditor { - public virtual string Text => SR.GetResourceString(nameof(SR.InstanceCreationEditorDefaultText), "(New...)"); + public virtual string Text => SR.UsingResourceKeys() ? "(New...)" : SR.InstanceCreationEditorDefaultText; /// /// This method is invoked when you user chooses the link displayed by the PropertyGrid for the InstanceCreationEditor. diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/MultilineStringConverter.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/MultilineStringConverter.cs index 151085de3cefa2..e91dccbaab302f 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/MultilineStringConverter.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/MultilineStringConverter.cs @@ -20,7 +20,7 @@ public class MultilineStringConverter : TypeConverter if (destinationType == typeof(string) && value is string) { - return SR.GetResourceString(nameof(SR.Text), "(Text)"); + return SR.UsingResourceKeys() ? "(Text)" : SR.Text; } return base.ConvertTo(context, culture, value, destinationType); diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ReferenceConverter.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ReferenceConverter.cs index 56ee100134d098..25e102f70943ba 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ReferenceConverter.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ReferenceConverter.cs @@ -15,7 +15,7 @@ namespace System.ComponentModel /// public class ReferenceConverter : TypeConverter { - private static readonly string s_none = SR.GetResourceString(nameof(SR.toStringNone), "(none)"); + private static readonly string s_none = SR.UsingResourceKeys() ? "(none)" : SR.toStringNone; private readonly Type _type; /// diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/TypeConverter.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/TypeConverter.cs index a91aac75a287a8..7b219e4f18b0fa 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/TypeConverter.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/TypeConverter.cs @@ -191,7 +191,7 @@ public virtual bool CanConvertTo(ITypeDescriptorContext? context, [NotNullWhen(t /// protected Exception GetConvertFromException(object? value) { - string? valueTypeName = value == null ? SR.GetResourceString(nameof(SR.Null), "(null)") : value.GetType().FullName; + string? valueTypeName = value == null ? (SR.UsingResourceKeys() ? "(null)" : SR.Null) : value.GetType().FullName; throw new NotSupportedException(SR.Format(SR.ConvertFromException, GetType().Name, valueTypeName)); } @@ -201,7 +201,7 @@ protected Exception GetConvertFromException(object? value) /// protected Exception GetConvertToException(object? value, Type destinationType) { - string? valueTypeName = value == null ? SR.GetResourceString(nameof(SR.Null), "(null)") : value.GetType().FullName; + string? valueTypeName = value == null ? (SR.UsingResourceKeys() ? "(null)" : SR.Null) : value.GetType().FullName; throw new NotSupportedException(SR.Format(SR.ConvertToException, GetType().Name, valueTypeName, destinationType.FullName)); } diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/TypeListConverter.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/TypeListConverter.cs index 3df91fba7d7254..1de41d8e39eae1 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/TypeListConverter.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/TypeListConverter.cs @@ -73,7 +73,7 @@ public override bool CanConvertTo(ITypeDescriptorContext? context, [NotNullWhen( { if (value == null) { - return SR.GetResourceString(nameof(SR.none), "(none)"); + return SR.UsingResourceKeys() ? "(none)" : SR.none; } else { diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/Timers/Timer.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/Timers/Timer.cs index 6d1d8cb5b8b0be..a9e0cfbb8fdcae 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/Timers/Timer.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/Timers/Timer.cs @@ -76,7 +76,7 @@ public Timer(TimeSpan interval) : this(interval.TotalMilliseconds) /// Gets or sets a value indicating whether the Timer raises the Tick event each time the specified /// Interval has elapsed, when Enabled is set to true. /// - [TimersDescription(nameof(SR.TimerAutoReset), null), DefaultValue(true)] + [TimersDescription(TimersDescriptionStringId.TimerAutoReset), DefaultValue(true)] public bool AutoReset { get => _autoReset; @@ -102,7 +102,7 @@ public bool AutoReset /// is able to raise events at a defined interval. /// The default value by design is false, don't change it. /// - [TimersDescription(nameof(SR.TimerEnabled), null), DefaultValue(false)] + [TimersDescription(TimersDescriptionStringId.TimerEnabled), DefaultValue(false)] public bool Enabled { get => _enabled; @@ -160,7 +160,7 @@ private void UpdateTimer() /// /// Gets or sets the interval on which to raise events. /// - [TimersDescription(nameof(SR.TimerInterval), null), DefaultValue(100d)] + [TimersDescription(TimersDescriptionStringId.TimerInterval), DefaultValue(100d)] public double Interval { get => _interval; @@ -184,7 +184,7 @@ public double Interval /// Occurs when the has /// elapsed. /// - [TimersDescription(nameof(SR.TimerIntervalElapsed), null)] + [TimersDescription(TimersDescriptionStringId.TimerIntervalElapsed)] public event ElapsedEventHandler Elapsed { add => _onIntervalElapsed += value; @@ -212,7 +212,7 @@ public override ISite? Site /// Gets or sets the object used to marshal event-handler calls that are issued when /// an interval has elapsed. /// - [DefaultValue(null), TimersDescription(nameof(SR.TimerSynchronizingObject), null)] + [DefaultValue(null), TimersDescription(TimersDescriptionStringId.TimerSynchronizingObject)] public ISynchronizeInvoke? SynchronizingObject { get diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/Timers/TimersDescriptionAttribute.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/Timers/TimersDescriptionAttribute.cs index 66ee30ec18372b..eb2500bc11b818 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/Timers/TimersDescriptionAttribute.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/Timers/TimersDescriptionAttribute.cs @@ -21,13 +21,21 @@ public class TimersDescriptionAttribute : DescriptionAttribute /// public TimersDescriptionAttribute(string description) : base(description) { } - /// - /// Constructs a new localized sys description. - /// - internal TimersDescriptionAttribute(string description, string? unused) : base(SR.GetResourceString(description)) + internal TimersDescriptionAttribute(TimersDescriptionStringId id) : base(GetResourceString(id)) { } + + private static string GetResourceString(TimersDescriptionStringId id) { - // Needed for overload resolution - Debug.Assert(unused == null); + switch (id) + { + case TimersDescriptionStringId.TimerAutoReset: return SR.TimerAutoReset; + case TimersDescriptionStringId.TimerEnabled: return SR.TimerEnabled; + case TimersDescriptionStringId.TimerInterval: return SR.TimerInterval; + case TimersDescriptionStringId.TimerIntervalElapsed: return SR.TimerIntervalElapsed; + case TimersDescriptionStringId.TimerSynchronizingObject: return SR.TimerSynchronizingObject; + default: + Debug.Fail($"Unexpected resource {id}"); + return ""; + } } /// @@ -49,4 +57,13 @@ public override string Description } } } + + internal enum TimersDescriptionStringId + { + TimerAutoReset, + TimerEnabled, + TimerInterval, + TimerIntervalElapsed, + TimerSynchronizingObject + } } diff --git a/src/libraries/System.Configuration.ConfigurationManager/tests/System.Configuration.ConfigurationManager.Tests.csproj b/src/libraries/System.Configuration.ConfigurationManager/tests/System.Configuration.ConfigurationManager.Tests.csproj index 2bbb45f5f7498e..e7881df77eb922 100644 --- a/src/libraries/System.Configuration.ConfigurationManager/tests/System.Configuration.ConfigurationManager.Tests.csproj +++ b/src/libraries/System.Configuration.ConfigurationManager/tests/System.Configuration.ConfigurationManager.Tests.csproj @@ -3,6 +3,7 @@ true $(NetCoreAppCurrent);$(NetFrameworkMinimum) true + $(DefineConstants);LEGACY_GETRESOURCESTRING_USER diff --git a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/StackTrace.cs b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/StackTrace.cs index e9c7ed4beef552..b82c61676404aa 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/StackTrace.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/StackTrace.cs @@ -197,10 +197,10 @@ internal void ToString(TraceFormat traceFormat, StringBuilder sb) { // Passing a default string for "at" in case SR.UsingResourceKeys() is true // as this is a special case and we don't want to have "Word_At" on stack traces. - string word_At = SR.GetResourceString(nameof(SR.Word_At), defaultString: "at"); + string word_At = SR.UsingResourceKeys() ? "at" : SR.Word_At; // We also want to pass in a default for inFileLineNumber. - string inFileLineNum = SR.GetResourceString(nameof(SR.StackTrace_InFileLineNumber), defaultString: "in {0}:line {1}"); - string inFileILOffset = SR.GetResourceString(nameof(SR.StackTrace_InFileILOffset), defaultString: "in {0}:token 0x{1:x}+0x{2:x}"); + string inFileLineNum = SR.UsingResourceKeys() ? "in {0}:line {1}" : SR.StackTrace_InFileLineNumber; + string inFileILOffset = SR.UsingResourceKeys() ? "in {0}:token 0x{1:x}+0x{2:x}" : SR.StackTrace_InFileILOffset; bool fFirstFrame = true; for (int iFrameIndex = 0; iFrameIndex < _numOfFrames; iFrameIndex++) { @@ -340,8 +340,7 @@ internal void ToString(TraceFormat traceFormat, StringBuilder sb) { sb.AppendLine(); // Passing default for Exception_EndStackTraceFromPreviousThrow in case SR.UsingResourceKeys is set. - sb.Append(SR.GetResourceString(nameof(SR.Exception_EndStackTraceFromPreviousThrow), - defaultString: "--- End of stack trace from previous location ---")); + sb.Append(SR.UsingResourceKeys() ? "--- End of stack trace from previous location ---" : SR.Exception_EndStackTraceFromPreviousThrow); } } } diff --git a/src/libraries/System.Text.Encoding.CodePages/src/System/Text/BaseCodePageEncoding.netcoreapp.cs b/src/libraries/System.Text.Encoding.CodePages/src/System/Text/BaseCodePageEncoding.netcoreapp.cs index 1158c7f66273f9..593c2f682f762e 100644 --- a/src/libraries/System.Text.Encoding.CodePages/src/System/Text/BaseCodePageEncoding.netcoreapp.cs +++ b/src/libraries/System.Text.Encoding.CodePages/src/System/Text/BaseCodePageEncoding.netcoreapp.cs @@ -66,12 +66,10 @@ internal static unsafe EncodingInfo [] GetEncodings(CodePagesEncodingProvider pr default: codePageName = new string(&codePageIndex.CodePageName); break; } - string? resourceName = EncodingNLS.GetLocalizedEncodingNameResource(codePageIndex.CodePage); string? displayName = null; - - if (resourceName != null && resourceName.StartsWith("Globalization_cp_", StringComparison.OrdinalIgnoreCase)) + if (!SR.UsingResourceKeys()) { - displayName = SR.GetResourceString(resourceName); + displayName = EncodingNLS.GetLocalizedEncodingNameResource(codePageIndex.CodePage); } encodingInfoList[i] = new EncodingInfo(provider, codePageIndex.CodePage, codePageName, displayName ?? codePageName); diff --git a/src/libraries/System.Transactions.Local/src/System/Transactions/Oletx/OletxTransaction.cs b/src/libraries/System.Transactions.Local/src/System/Transactions/Oletx/OletxTransaction.cs index b778978da8df4d..753a52211ec0a9 100644 --- a/src/libraries/System.Transactions.Local/src/System/Transactions/Oletx/OletxTransaction.cs +++ b/src/libraries/System.Transactions.Local/src/System/Transactions/Oletx/OletxTransaction.cs @@ -552,7 +552,7 @@ internal Guid Identifier // for COM+ interop purposes, but we can't get the guid or the status of the transaction. if (TxGuid.Equals(Guid.Empty)) { - throw TransactionException.Create(SR.GetResourceString(SR.CannotGetTransactionIdentifier), null); + throw TransactionException.Create(SR.CannotGetTransactionIdentifier, null); } return TxGuid;