I have this class called BaseMessage
that contains string field Message
:
public class BaseMessage
{
public string Message;
...
}
And I have another class ProcessMessage
that derives from BaseMessage
:
public class ProcessMessage : BaseMessage
{
...
}
Overall this class is created in 500 different places across entire project. Sometimes string is interpolated:
var ret = new ProcessMessage() { Message = $"Some information {someParam}" };
Sometimes it isn't:
var ret = new ProcessMessage() { Message = "Some information" };
Sometimes it is passed as a string variable, like this:
var ret = new ProcessMessage() { Message = someMessage };
What I need to achieve is to somehow store the template of interpolated string.
I just learned you can use FormattableString
class for it.
What I have tried
I tried to overload the Message
field like this:
public class ProcessMessage : BaseMessage
{
public new FormattableString Message;
...
}
But this approach wouldn't compile, because you can't assign normal string to FormattableString
object. I'm totally ok for normal string to be stored in Format
property of FormattableString
. But there seems to be no proper way to convert string to FormattableString
without editing the entire project or I couldn't find one.
I also tried to overload assign operator to chose the type in a runtime:
public class SpecialString
{
public string StringValue { get; private set; }
public FormattableString FormattableValue { get; private set; }
public static implicit operator SpecialString(string value)
{
return new SpecialString { StringValue = value };
}
public static implicit operator SpecialString(FormattableString value)
{
return new SpecialString { FormattableValue = value };
}
public override string ToString()
{
return FormattableValue?.ToString() ?? StringValue ?? string.Empty;
}
}
But this approach didn't work. C# always chooses a normal string.
I thought maybe you could create class that would derive from FormattableString
, but I'm not sure how.
I have this class called BaseMessage
that contains string field Message
:
public class BaseMessage
{
public string Message;
...
}
And I have another class ProcessMessage
that derives from BaseMessage
:
public class ProcessMessage : BaseMessage
{
...
}
Overall this class is created in 500 different places across entire project. Sometimes string is interpolated:
var ret = new ProcessMessage() { Message = $"Some information {someParam}" };
Sometimes it isn't:
var ret = new ProcessMessage() { Message = "Some information" };
Sometimes it is passed as a string variable, like this:
var ret = new ProcessMessage() { Message = someMessage };
What I need to achieve is to somehow store the template of interpolated string.
I just learned you can use FormattableString
class for it.
What I have tried
I tried to overload the Message
field like this:
public class ProcessMessage : BaseMessage
{
public new FormattableString Message;
...
}
But this approach wouldn't compile, because you can't assign normal string to FormattableString
object. I'm totally ok for normal string to be stored in Format
property of FormattableString
. But there seems to be no proper way to convert string to FormattableString
without editing the entire project or I couldn't find one.
I also tried to overload assign operator to chose the type in a runtime:
public class SpecialString
{
public string StringValue { get; private set; }
public FormattableString FormattableValue { get; private set; }
public static implicit operator SpecialString(string value)
{
return new SpecialString { StringValue = value };
}
public static implicit operator SpecialString(FormattableString value)
{
return new SpecialString { FormattableValue = value };
}
public override string ToString()
{
return FormattableValue?.ToString() ?? StringValue ?? string.Empty;
}
}
But this approach didn't work. C# always chooses a normal string.
I thought maybe you could create class that would derive from FormattableString
, but I'm not sure how.
1 Answer
Reset to default 1This implicit conversion does not work the way you intend to (from string
in code to FormattableString
by the compiler and to SpecialString
from our custom conversion:
public static implicit operator SpecialString(FormattableString value) {
return new SpecialString { FormattableValue = value };
}
you can test it by removing the other implicit conversion from string
.
So, the only option I think you are left with is factory methods on SpecialString
. One downside is that string
is preferred over FormattableString
even when the string is interpolated - so you'd have to have two different Create
and CreateFormatted
which isn't ideal.
In .NET6+ the feature of custom InterpolatedStringHandler
s can help with this shortcoming because the compiler prefers the "correct" overload - allowing us to have just one Create
method. The code below basically uses a the custom handler to build manually a FormattableString
so we can get the Format
and GetArguments
from it:
public sealed class SpecialString {
public string StringValue { get; private set; }
public FormattableString FormattableString { get; private set; }
private SpecialString() { }
public static SpecialString Create(string value) => new SpecialString { StringValue = value };
public static SpecialString Create(FormattableInterpolatedStringHandler builder) {
var result = new SpecialString();
result.StringValue = builder.GetFormattedText();
result.FormattableString = builder.FormattableString;
return result;
}
// Optional
public static implicit operator string(SpecialString value) => value?.StringValue;
// or this
public override string ToString() => StringValue;
}
[InterpolatedStringHandler]
public struct FormattableInterpolatedStringHandler {
StringBuilder _builder;
object[] _arguments;
int _counter;
public FormattableString FormattableString { get; private set; }
public FormattableInterpolatedStringHandler(int literalLength, int formattedCount) {
_builder = new StringBuilder(literalLength);
_arguments = new object[formattedCount];
}
public void AppendLiteral(string s) {
_builder.Append(s);
}
public void AppendFormatted<T>(T t) {
_builder.Append($"{{{_counter}}}");
_arguments[_counter] = t;
_counter++;
}
public string GetFormattedText() {
FormattableString = new InterPolatedFormattable(_builder.ToString(), _arguments);
return FormattableString.ToString();
}
}
public class InterPolatedFormattable : FormattableString {
object[] _arguments;
string _format;
public InterPolatedFormattable(string format, object[] arguments) {
_format = format;
_arguments = arguments;
}
public override int ArgumentCount => _arguments.Length;
public override string Format => _format;
public override object GetArgument(int index) => _arguments[index];
public override object[] GetArguments() => _arguments;
public override string ToString(IFormatProvider formatProvider) {
return string.Format(formatProvider, _format, _arguments);
}
}
And use:
var someInt = 44;
SpecialString foo = SpecialString.Create($"hello {DateTime.Now} and {someInt}");
Console.WriteLine(foo); // hello 07-Mar-25 12:32:41 PM and 44
Console.WriteLine(foo.StringValue); // hello 07-Mar-25 12:32:41 PM and 44
Console.WriteLine(foo.FormattableString.Format); // hello {0} and {1}
Console.WriteLine(foo.FormattableString.GetArguments()[0]); // 2025...
发布者:admin,转转请注明出处:http://www.yc00.com/questions/1744939337a4602225.html
string
there isn't actually a template which is then formatted with some given input. You can see the decompilation here of how it works under the hood. – MindSwipe Commented Mar 7 at 9:30