my first source code generator, yea!.. and a question
-
Thanks to the newsletter article I finally write my very first own source code generator, yea! :) I found none of the resource on the topic I found were both simple and super helpful, but the cumulative tips in all of them finally unlocked the skill for me! Turns out it's very simple (the main hurdle is that your generator library need to target .Net Standard 2.0).. someone need to write a "source code generator for dummy" article distilled to the essential! ;P (hey, maybe that's an idea hey?) Anyway... I still am left with one.. conundrum? I made a .resx to C# code generator. I know those exists out of the box, but the resulting class doesn't support static binding event if the current culture change, which is annoying with the live culture update support in my application. I have a large chunk of code to enable live culture update, but I thought, wouldn't it be nice if I could have a different C# resource class hey? And now I have! my conundrum is, the default resx class's property looks like that
internal static string About { get { return ResourceManager.GetString("About", resourceCulture); } }
whereas mine looks like that
public static string? fsf { get; private set; } public static string? hello { get; private set; } public static string? name { get; private set; } static partial void UpdateValues() { var resources = GetDictionaries(Culture); fsf = GetResource("fsf", resources); hello = GetResource("hello", resources); name = GetResource("name", resources); }
so the the default class pay a (negligible) cost on every property access, and my code pay another (negligible but bigger) cost on resource change (but none on property access). and I keep wondering, which one is the best? for context, this class is primarily used through databinding to inform a WPF UI.
A new .NET Serializer All in one Menu-Ribbon Bar Taking over the world since 1371!
-
Thanks to the newsletter article I finally write my very first own source code generator, yea! :) I found none of the resource on the topic I found were both simple and super helpful, but the cumulative tips in all of them finally unlocked the skill for me! Turns out it's very simple (the main hurdle is that your generator library need to target .Net Standard 2.0).. someone need to write a "source code generator for dummy" article distilled to the essential! ;P (hey, maybe that's an idea hey?) Anyway... I still am left with one.. conundrum? I made a .resx to C# code generator. I know those exists out of the box, but the resulting class doesn't support static binding event if the current culture change, which is annoying with the live culture update support in my application. I have a large chunk of code to enable live culture update, but I thought, wouldn't it be nice if I could have a different C# resource class hey? And now I have! my conundrum is, the default resx class's property looks like that
internal static string About { get { return ResourceManager.GetString("About", resourceCulture); } }
whereas mine looks like that
public static string? fsf { get; private set; } public static string? hello { get; private set; } public static string? name { get; private set; } static partial void UpdateValues() { var resources = GetDictionaries(Culture); fsf = GetResource("fsf", resources); hello = GetResource("hello", resources); name = GetResource("name", resources); }
so the the default class pay a (negligible) cost on every property access, and my code pay another (negligible but bigger) cost on resource change (but none on property access). and I keep wondering, which one is the best? for context, this class is primarily used through databinding to inform a WPF UI.
A new .NET Serializer All in one Menu-Ribbon Bar Taking over the world since 1371!
I like MS's for the modifiability but assuming yours is generated I guess it doesn't matter so much. Why the nullable string type though? It's already a reference type. If they're all strings I'd ditch making it nullable to keep it simple. Then you can just use a basic null check on it. Unless there's some reason for it I don't get. I wouldn't care about the performance unless you're databinding to a real time graph or something (which I've done) Dictionaries are pretty quick though but the main issue I see with your UpdateValues() routine (given I don't fully understand what it accomplishes) is that if you have a lot of resources it will end up being slow. Have you considered using
Lazy<T>
and loading on demand on a property by property basis? Lazy<T> Class (System) | Microsoft Docs[^] You can dump one behind eachget
accessor so it loads the resource the first time on demand. That way you're only loading what you use, and you're not potentially causing a burp in your application's responsiveness when UpdateValues() is called for a lot of resources. But then I'm spitballing, not having seen all of the code.Real programmers use butterflies
-
I like MS's for the modifiability but assuming yours is generated I guess it doesn't matter so much. Why the nullable string type though? It's already a reference type. If they're all strings I'd ditch making it nullable to keep it simple. Then you can just use a basic null check on it. Unless there's some reason for it I don't get. I wouldn't care about the performance unless you're databinding to a real time graph or something (which I've done) Dictionaries are pretty quick though but the main issue I see with your UpdateValues() routine (given I don't fully understand what it accomplishes) is that if you have a lot of resources it will end up being slow. Have you considered using
Lazy<T>
and loading on demand on a property by property basis? Lazy<T> Class (System) | Microsoft Docs[^] You can dump one behind eachget
accessor so it loads the resource the first time on demand. That way you're only loading what you use, and you're not potentially causing a burp in your application's responsiveness when UpdateValues() is called for a lot of resources. But then I'm spitballing, not having seen all of the code.Real programmers use butterflies
I decided my option is better.. because it's better to waste (negligible) time on user action (when they change the current culture) instead of while simply viewing data.
honey the codewitch wrote:
Why the nullable string type though?
It's just the new syntactic sugar with nullable reference type (which is just a compiler hint, not a runtime truth). Arguably it's true they wont be null because the default culture dictionary will not be empty (with the way it's constructed). null string never bothered me, so I didn't give it a thought.. but yea.. it might a nice touch.
honey the codewitch wrote:
Have you considered using Lazy<T>
Well this is clearly counter productive, because the whole point of this generator is that the culture might (will?) change. Lazy have fixed value! (I particularly wants live change notification since I opened a software in a unintelligible foreign language and I tried to make it English, turns out I had to find the language menu, select "english" and restart... Thanks google or I would have given up) What you might suggest (and was contemplating) is to have my properties like so instead. Which remove the need for
UpdateValues()
, this is also more like the MS one is doing it.public static string? hello => GetResource("hello", GetDictionaries(Culture));
A new .NET Serializer All in one Menu-Ribbon Bar Taking over the world since 1371!
-
I decided my option is better.. because it's better to waste (negligible) time on user action (when they change the current culture) instead of while simply viewing data.
honey the codewitch wrote:
Why the nullable string type though?
It's just the new syntactic sugar with nullable reference type (which is just a compiler hint, not a runtime truth). Arguably it's true they wont be null because the default culture dictionary will not be empty (with the way it's constructed). null string never bothered me, so I didn't give it a thought.. but yea.. it might a nice touch.
honey the codewitch wrote:
Have you considered using Lazy<T>
Well this is clearly counter productive, because the whole point of this generator is that the culture might (will?) change. Lazy have fixed value! (I particularly wants live change notification since I opened a software in a unintelligible foreign language and I tried to make it English, turns out I had to find the language menu, select "english" and restart... Thanks google or I would have given up) What you might suggest (and was contemplating) is to have my properties like so instead. Which remove the need for
UpdateValues()
, this is also more like the MS one is doing it.public static string? hello => GetResource("hello", GetDictionaries(Culture));
A new .NET Serializer All in one Menu-Ribbon Bar Taking over the world since 1371!
I didn't realize your culture changed over the life of the application. You could still do this, but you'd just have to reset your lazy init. If Lazy won't let you do it you can make your own poor man's lazy using lock(object) {} but you don't even need to do that unless your object will be accessed from multiple threads. If it's not, you can just do
if(myres==null) {
// fetch my res
}
return myres;and then in your update routine you just set myres to null.
Real programmers use butterflies
-
I didn't realize your culture changed over the life of the application. You could still do this, but you'd just have to reset your lazy init. If Lazy won't let you do it you can make your own poor man's lazy using lock(object) {} but you don't even need to do that unless your object will be accessed from multiple threads. If it's not, you can just do
if(myres==null) {
// fetch my res
}
return myres;and then in your update routine you just set myres to null.
Real programmers use butterflies
mmm... I guess this is a (negligible) over my (negligible) update, which might save a significant amount of negligible time! mmm.. worth it! :)
A new .NET Serializer All in one Menu-Ribbon Bar Taking over the world since 1371!
-
I didn't realize your culture changed over the life of the application. You could still do this, but you'd just have to reset your lazy init. If Lazy won't let you do it you can make your own poor man's lazy using lock(object) {} but you don't even need to do that unless your object will be accessed from multiple threads. If it's not, you can just do
if(myres==null) {
// fetch my res
}
return myres;and then in your update routine you just set myres to null.
Real programmers use butterflies
new generated code thanks to you! :) putting it all, just for fun.... (the other partial file is always the same and jut copied from a generator resource)
public partial class TestResource { public static string fsf => \_fsf ?? (\_fsf = GetResource("fsf", GetDictionaries(Culture))); public static string hello => \_hello ?? (\_hello = GetResource("hello", GetDictionaries(Culture))); public static string name => \_name ?? (\_name = GetResource("name", GetDictionaries(Culture))); private static string? \_fsf; private static string? \_hello; private static string? \_name; static partial void UpdateValues() { \_fsf = null; \_hello = null; \_name = null; } static TestResource() { AddCultureDictionary("", new Dictionary { { "fsf", "ffs" }, { "hello", "hello" }, { "name", "name" }, }); AddCultureDictionary("fr-FR", new Dictionary { { "fsf", "nom de dieu" }, { "hello", "salut" }, { "name", "nom" }, }); } }
A new .NET Serializer All in one Menu-Ribbon Bar Taking over the world since 1371!
-
new generated code thanks to you! :) putting it all, just for fun.... (the other partial file is always the same and jut copied from a generator resource)
public partial class TestResource { public static string fsf => \_fsf ?? (\_fsf = GetResource("fsf", GetDictionaries(Culture))); public static string hello => \_hello ?? (\_hello = GetResource("hello", GetDictionaries(Culture))); public static string name => \_name ?? (\_name = GetResource("name", GetDictionaries(Culture))); private static string? \_fsf; private static string? \_hello; private static string? \_name; static partial void UpdateValues() { \_fsf = null; \_hello = null; \_name = null; } static TestResource() { AddCultureDictionary("", new Dictionary { { "fsf", "ffs" }, { "hello", "hello" }, { "name", "name" }, }); AddCultureDictionary("fr-FR", new Dictionary { { "fsf", "nom de dieu" }, { "hello", "salut" }, { "name", "nom" }, }); } }
A new .NET Serializer All in one Menu-Ribbon Bar Taking over the world since 1371!
Sweet. Glad to help. Edit: I was thinking of the case where you do like microsoft and put every error message as a resource. In resource files like that you have hundreds or thousands of resources, and the above code will handle it much better than trying to load them all at once. I figured it was worth it because this is a code generator, so the uses of it are somewhat open ended. IOW some day someone (maybe you) will use it to generate a particularly large resource and you'll be glad it works this way. =)
Real programmers use butterflies
-
Sweet. Glad to help. Edit: I was thinking of the case where you do like microsoft and put every error message as a resource. In resource files like that you have hundreds or thousands of resources, and the above code will handle it much better than trying to load them all at once. I figured it was worth it because this is a code generator, so the uses of it are somewhat open ended. IOW some day someone (maybe you) will use it to generate a particularly large resource and you'll be glad it works this way. =)
Real programmers use butterflies
In fact... your comment scratched a itch.. and I made a new generator that use an underlying
ResourceManager
(like Microsoft) instead of static dictionaries! yeah! :D Super witch to the rescue! :)A new .NET Serializer All in one Menu-Ribbon Bar Taking over the world since 1371!
-
Thanks to the newsletter article I finally write my very first own source code generator, yea! :) I found none of the resource on the topic I found were both simple and super helpful, but the cumulative tips in all of them finally unlocked the skill for me! Turns out it's very simple (the main hurdle is that your generator library need to target .Net Standard 2.0).. someone need to write a "source code generator for dummy" article distilled to the essential! ;P (hey, maybe that's an idea hey?) Anyway... I still am left with one.. conundrum? I made a .resx to C# code generator. I know those exists out of the box, but the resulting class doesn't support static binding event if the current culture change, which is annoying with the live culture update support in my application. I have a large chunk of code to enable live culture update, but I thought, wouldn't it be nice if I could have a different C# resource class hey? And now I have! my conundrum is, the default resx class's property looks like that
internal static string About { get { return ResourceManager.GetString("About", resourceCulture); } }
whereas mine looks like that
public static string? fsf { get; private set; } public static string? hello { get; private set; } public static string? name { get; private set; } static partial void UpdateValues() { var resources = GetDictionaries(Culture); fsf = GetResource("fsf", resources); hello = GetResource("hello", resources); name = GetResource("name", resources); }
so the the default class pay a (negligible) cost on every property access, and my code pay another (negligible but bigger) cost on resource change (but none on property access). and I keep wondering, which one is the best? for context, this class is primarily used through databinding to inform a WPF UI.
A new .NET Serializer All in one Menu-Ribbon Bar Taking over the world since 1371!
How about generating methods for resource strings with placeholders? :) For example, if your resource string
Greeting
contains"Hello, {0}. The date is {1:D}."
, your code could generate something like:private static string _greeting;
public static string Greeting(object p0, object p1)
{
string value = _greeting ??= GetResource("Greeting", GetDictionaries(Culture));
return string.Format(value, p0, p1);
}
"These people looked deep within my soul and assigned me a number based on the order in which I joined." - Homer
-
How about generating methods for resource strings with placeholders? :) For example, if your resource string
Greeting
contains"Hello, {0}. The date is {1:D}."
, your code could generate something like:private static string _greeting;
public static string Greeting(object p0, object p1)
{
string value = _greeting ??= GetResource("Greeting", GetDictionaries(Culture));
return string.Format(value, p0, p1);
}
"These people looked deep within my soul and assigned me a number based on the order in which I joined." - Homer
bah, the class is mostly here for live databinding of resource string in WPF UI. My class fire the static change event supported by bindings! (unlike Microsoft one, obviously)
A new .NET Serializer All in one Menu-Ribbon Bar Taking over the world since 1371!