.net - Is there a way to store a template of interpolated string in C#? - Stack Overflow

I have this class called BaseMessage that contains string field Message:public class BaseMessage{publ

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.

Share Improve this question edited Mar 7 at 9:29 marc_s 756k184 gold badges1.4k silver badges1.5k bronze badges asked Mar 7 at 9:20 bleat interteimentbleat interteiment 1,4672 gold badges13 silver badges17 bronze badges 6
  • String interpolation is "just" syntax sugar that ultimately constructs a regular old 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
  • 3 This seems like an XY problem, what problem are you actually trying to solve? – MindSwipe Commented Mar 7 at 9:31
  • You are right. But if compiler see's explicitly that you want FormattableString, than it will actually create an object that will store the template of the strings and parameters. Read about FormattableString please. – bleat interteiment Commented Mar 7 at 9:39
  • 1 I also think this is an XY problem. Please edit your question to provide details and some examples of your actual problem. – shingo Commented Mar 7 at 10:00
  • 1 @shingo This is not XY problem. If I won't be able to store template of interpolated string - than the only other option I have is manual work. I don't want you to solve my original problem, I need help with my technical issue, not architectural approach. – bleat interteiment Commented Mar 7 at 10:16
 |  Show 1 more comment

1 Answer 1

Reset to default 1

This 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 InterpolatedStringHandlers 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

相关推荐

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信