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;