Having string resources in Strings.resx/Strings.Designer.cs, used like this:
myObject.DoSomething(Strings.HelloWorld);
can I capture the resource id (i.e. "Strings.HelloWorld") in DoSomething without changing the source line that calls DoSomething? I want the name of the static property defined in Strings.Designer.cs, along with the string value.
What I've figured out is:
myObject.DoSomething(() => Strings.HelloWorld);
and then:
void DoSomething(Expression<Func<string>> expr)
{
string id = expr.Body.ToString();
string value = (expr.Compile())();
// do something with these...
}
That changes the original line slightly, but it's still readable and the compiler is checking the property name for me.
I was wondering if there is any C# magic to do this without changing that original line.
EDIT: Really, I just want unique ints for each string resource that I use to format a TraceEvent message, to use as the TraceEvent id. I don't want to maintain a list of ints myself when the compiler can do that for me, adding one every time I insert a new source line to trace something else interesting. I get my ints once in initialization by mapping the ResourceSet keys to a sequence of ints, and I don't mind if the numbers shift around from build to build.
EDIT2: Referring to LBushkin's answer, I did initially map strings to int, like this:
var resourceSet = resourceManager.GetResourceSet(CultureInfo.CurrentCulture, true, true);
var sorted =
from i in resourceSet.Cast<DictionaryEntry>()
orderby i.Key
select i.Value as string;
var valueToId = new Dictionary<string, int>();
foreach (string value in sorted)
{
valueToId[value] = valueToId.Count + 100; // whatever range you like
}
...but I don't like relying on a static CurrentCulture. I want a simple low-tech, low-maintenance mapping of the resource strings to unique ints, where all I need to do is add strings in the designer when I feel like it.
No, I'm afraid there isn't anything that will do this. The lambda expression is the closest you'll get.
EDIT: Warning - smelly code ahead
At least, that's the case if you don't want to add extra methods in another Strings class in the same namespace as the caller. Here's a really grotty hack:
// You'd want to autogenerate this class based on the designer file.
public class Strings
{
public static string HelloWorld()
{
return global::OriginalNS.Strings.HelloWorld;
}
}
then...
void DoSomething(Func<string> provider)
{
string id = provider.Method.Name;
string value = provider();
}
Now this:
myObject.DoSomething(Strings.HelloWorld);
is compiled into a method group conversion using the method Strings.HelloWorld
. DoSomething
finds the name of that method to get the ID, and calls the delegate to get the actual value.
This will only work if Strings
is in a different namespace to the method calling DoSomething
, so that you can hijack the use of the name Strings
. I suppose another alternative would be a using directive:
using Strings = EvilStrings;
In general though: urgh! Don't do this! It's fun to think about, of course, but I wouldn't wish it on production code...
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With