I have a class like this:
public class DbSettingsValues : Dictionary<string, string>
{
public DbSettingsValues() { }
public DbSettingsValues(Dictionary<string, string> values) : base(values) { }
public string EmailApiServer
{
get => this.GetValueOrDefault(DbSettingsKeys.EmailApiServer, "");
set => this[DbSettingsKeys.EmailApiServer] = value;
}
public string EmailApiKey
{
get => this.GetValueOrDefault(DbSettingsKeys.EmailApiKey, "");
set => this[DbSettingsKeys.EmailApiKey] = value;
}
// about 15 more...
// There is also a few difference, like this one:
public int RegisterFirstEmailDelay
{
get => int.Parse(this.GetValueOrDefault(DbSettingsKeys.RegisterFirstEmailDelay, "24"));
set => this[DbSettingsKeys.RegisterFirstEmailDelay] = value.ToString();
}
}
So for every property I need to put the exact name 3 times. I understand this would be bad for maintenance later and prone to mistakes when I make more properties by copying (i.e. forget to replace the name in 1 of the 3 places).
Is there a better way to deal with this? Some solutions came to my mind:
Something like the old T4, I don't see the T4 menu anymore in VS2022, so I assume it doesn't work on .NET Core?
Code Generator seem to require an external project. I assume this is how EF Core Scaffolding works? Can someone suggest a keyword/link to what I should look for? I.e. read a C# project, look for properties inside a class and generate a
.cs
file accordingly?
As of current specs, the key text is just the same:
public const string EmailApiServer = nameof(EmailApiServer);
public const string EmailApiKey = nameof(EmailApiKey);
// ...
So technically I can just replace DbSettingsKeys.EmailApiServer
to nameof(EmailApiServer)
for example, but I am not sure it will stay the same later. Is it a good idea to proceed with this now and think about it later when specs change?
Update: I just realized that even with nameof(PropertyName)
, it's still prone to copying issue since there is no error if I forget to change the name:
// No error here because the name is valid
public string EmailApiKey
{
get => this.GetValueOrDefault(nameof(EmailApiServer), "");
set => this[nameof(EmailApiServer)] = value;
}
I think something like CallerMemberNameAttribute
maybe useful but I don't actually know how it works.
CodePudding user response:
If the keys really have the same names as the properties, you can indeed use [CallerMemberName]
attributes to reduce the number of times that you have to type the property name.
For example:
public class DbSettingsValues : Dictionary<string, string>
{
public string EmailApiServer
{
get => getProperty();
set => setProperty(value);
}
public string EmailApiKey
{
get => getProperty();
set => setProperty(value);
}
public int RegisterFirstEmailDelay
{
get => getProperty(24);
set => setProperty(value);
}
string getProperty(string defaultValue = "", [CallerMemberName] string? propName = default)
{
return this!.GetValueOrDefault(propName, defaultValue);
}
T getProperty<T>(T defaultValue, [CallerMemberName] string? propName = default)
{
var result = this!.GetValueOrDefault(propName, defaultValue!.ToString());
return (T) Convert.ChangeType(result, typeof(T))!;
}
void setProperty(string value, [CallerMemberName] string? propName = default)
{
this[propName!] = value;
}
void setProperty<T>(T value, [CallerMemberName] string? propName = default)
{
this[propName!] = value!.ToString()!;
}
}
It's not perfect, but it does reduce the chance of typos significantly.