简体   繁体   中英

Function with IFormattable parameter won't accept a String

I have a function where I processed a string input.

    public string Foo(string text)
    {
        // do stuff with text and
        // return processed string
    }

I was calling this from a lot of places where I was converting a guid to a string like this:

string returnValue = Foo(bar.ToString());

What I actually wanted was to accept as an input any object type that could be converted to a string. So I tried modifying the function as follows:

    public string Foo(IFormattable text)
    {
        var textAsString = text.ToString();
        // do stuff with textAsString 
        // and return processed string
    }

This means all my calls are simpler:

string returnValue = Foo(bar);

It works for all object types that have a .ToString method; Except strings :)

If I try passing a string to the function I get the following compile error:

Argument type 'string' is not assignable to parameter type 'System.IFormattable'  

Which seems really odd because String HAS a ToString() method.

Why doesn't this work?

Simply put, System.String doesn't implement IFormattable .

If the documentation isn't enough for you:

object x = "a string";
Console.WriteLine(x is IFormattable); // False

Given that ToString() is declared on object , why not just have:

public string Foo(object text)
{
    var textAsString = text.ToString();
    // do stuff with textAsString 
    // and return processed string
}

Indeed, the ToString() method declared by IFormattable isn't the one you were trying to call anyway - you weren't passing a format string or a format provider.

Additionally:

Which seems really odd because String HAS a ToString() method.

Interfaces aren't duck-typed. Just because a type has all the members required by an interface doesn't mean that it implements the interface.

First of all, IFormatable does not implment .ToString() , it only implments .ToString(string, IFormatProvider) . With that knowledge in hand, if you still want to use IFormatable the easiest way to handle this is just use overloads for your methods.

public string Foo(IFormattable text, string format, IFormatProvider formatProvider)
{
    return Foo(text.ToString(format, formatProvider));
}

public string Foo(IFormattable text, string format)
{
    return Foo(text.ToString(format, null));
}

public string Foo(string text)
{
    // do stuff with text and
    // return processed string
}

String does not implement IFormattable , but your code does not use it either. You can create a wrapper class for a string to be used in scenarios where IFormattable is needed, like this:

public class FormattableString : IFormattable {
    private readonly string str;
    public FormattableString(string str) {
        this.str = str;
    }
    public string ToString() {
        return str;
    }
    public string ToString(string format, IFormatProvider formatProvider) {
        // You can add special code here to format the value of your string
        // as directed by the format passed into the method.
        return str;
    }
}

Now you can use this class as follows:

string returnValue = Foo(new FormattableString(bar));

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM