How to properly implement `ISpanFormattable.TryFormat()`? - c#

I have a class that implements IFormattable and I want to also implement ISpanFormattable as defined in C# 7
public partial interface ISpanFormattable : System.IFormattable
{
bool TryFormat(Span<char> destination, out int charsWritten, ReadOnlySpan<char> format, IFormatProvider? provider);
}
Here is the implementation that I came up with, but I am not sure if this follows the logic it is intended to have. I am unclear about when to return true/false and how to handle the arguments correctly.
public class Foo : ISpanFormattable
{
// other definitions
#region Formatting
public override string ToString() => ToString("g");
public string ToString(string? formatting) => ToString(formatting, CultureInfo.CurrentCulture.NumberFormat);
public string ToString(string? formatting, IFormatProvider? formatProvider)
{
// code to return a formatted string
}
public bool TryFormat(Span<char> destination, out int charsWritten, ReadOnlySpan<char> format, IFormatProvider? provider)
{
string formatting = format.ToString();
string result = this.ToString(formatting, provider);
charsWritten = Math.Min(result.Length, destination.Length);
result.CopyTo(destination[..charsWritten] );
return charsWritten == result.Length;
}
#endregion
}
What I have done is convert the ReadOnlySpan<char> format into a string in order to call the existing ToString() function, and then use the result to copy into the destination. The function will return false if the resulting string is larger than the destination Span<char>.
It seems a bit long for a basic wrapper around ToString(), and might be a performance bottleneck when converting Span<char> to string.
Is there a simpler/better way to implement TryFormat() for my class?

Related

Getting Input of a Required Type: Solution Using Generics?

A long time ago in a galaxy far, far away, I posted an answer to this question here about input and such.
The question dealt with avoiding multiple ReadLine() statements to get user input and a way around it to clean up code. I posted a simple answer with a prompt method that displayed the prompt and returned the input. However, it always returned the string. Now, one could implement their own parsing to extract the information they wanted, but what if Generics could simplify things and return the type desired?
I thought it would be easy. So I tried to do it as a simple exercise. Turns out, I don't think I'm smart enough (oops).
I first tried a helper method inside a Generic SomeClass<T> like
public static T getInput(String prompt, Type T)
{
//some stuff about printing the prompt
String input = Console.In.ReadLine();
return (T)input;
}
As many of you will no doubt see, this is faulty and returns the error "cannot cast String to type T."
My next approach was to use SomeClass<T> where T : String
Again, many of you will see the fault here: String is sealed.
So: the question is, is there a way to use the method I've outlined and Generics to correctly grab user input and return it in the type requested, like int, String, or possibly a user-defined type?
I think Convert.ChangeType would probably fit the bill here, although it would fail horribly with the wrong input. Exercise for OP.
public static T GetInput<T>(string prompt)
{
//some stuff about printing the prompt
string input = Console.ReadLine();
return (T)Convert.ChangeType(input, typeof(T));
}
So now:
float a = GetInput<float>("enter a float:");
You could probably harden the code by looking at this question and its answers.
Based on the answer from https://stackoverflow.com/a/1833128/4780742 by Tim Coker (who credits "Tuna Toksoz"), you can use TypeDescriptor to get a converter for any type. The reason why TypeDescriptor is preferable to Convert.ChangeType is because it can work for nullables and user-defined classes if the converter for the user-defined class is implemented.
public static class TConverter
{
public static T ChangeType<T>(object value)
{
return (T)ChangeType(typeof(T), value);
}
public static object ChangeType(Type t, object value)
{
TypeConverter tc = TypeDescriptor.GetConverter(t);
return tc.ConvertFrom(value);
}
public static void RegisterTypeConverter<T, TC>() where TC : TypeConverter
{
TypeDescriptor.AddAttributes(typeof(T), new TypeConverterAttribute(typeof(TC)));
}
}
The RegisterTypeConverter is so that you don't have to use reflection if you plan on converting a specific type many times over.
Note that conversion to certain types from a string is extremely reliant on culture settings. For instance DateTime parsing will be extremely hard to do. For numbers, decimal separators, currency symbols etc vary by culture.
There should be a retry mechanism for when the user mistypes or provides invalid input, notifying the user.
static T ReadInput<T>(string prompt)
{
return ReadInput<T>(prompt, CultureInfo.CurrentCulture);
}
static T ReadInput<T>(string prompt, IFormatProvider formatProvider)
{
bool validInput = false;
T result = default(T);
Console.WriteLine("Prompt");
while (!validInput)
{
try
{
result = (T)Convert.ChangeType(Console.ReadLine(), typeof (T), formatProvider);
validInput = true;
}
catch
{
Console.WriteLine("Could not interpret value, please try again.");
}
}
return result;
}
Note that there is no way to make this user friendly. Consider defining a converter interface that can provide an error message when parsing fails as well as safely try to convert:
interface IConvertStringTo<T>
{
bool TryGetValue(string input, IFormatProvider formatProvider, out T value);
string ErrorMessage { get; }
}
class IntegerParser : IConvertStringTo<int>
{
public bool TryGetValue(string input, IFormatProvider formatProvider, out int value)
{
return int.TryParse(input, NumberStyles.Integer, formatProvider, out value);
}
public string ErrorMessage
{
get { return "Please enter a number."; }
}
}

Extend a type with customized class

I'm wondering something. Instead of writing String.Format("{0:X}", num); to convert numbers to hex. Is there a way where i could extend string directly so that i could simply write num.ToHex(); instead?
You can create extension method:
public static class IntExtensions
{
public static string ToHex(this int source)
{
return string.Format("{0:X}", source);
}
}
Execute like this:
string hexNum = 1234.ToHex();
It's called extension method. However, it should be set on numeric type, to allow {0:X} string format:
public static class Extensions
{
public static string ToHex(this int source)
{
return string.Format("{0:X}", source);
}
}

How to implement a "convert using this function" custom attribute in .Net?

I'm building some stuff out using Attributes. One thing I'd really like to implement as an attribute is a convert a string to this property's type using this function. Right now, I have this:
public delegate object ParameterConverter(string val);
[AttributeUsage(AttributeTargets.Property)]
public class ParameterConverterAttribute : ParameterBaseAttribute
{
ParameterConverter Converter;
public ParameterConverterAttribute(ParameterConverter converter)
{
Converter=converter;
}
public object Convert(string val)
{
return Converter(val);
}
}
And I use it like so:
public class Tester
{
[ParameterConverter(new ParameterConverter(TestConverter)] //error here
public int Foo{get;set;}
static object TestConverter(string val)
{
return 10;
}
}
However, .Net or at least C# doesn't appear to support this kind of thing. It appears that delegates inside of attributes doesn't work.
Is there any workarounds to this issue or a good way to deal with this problem?
No Delegates cannot be passed as an argument to an Attribute. The Supported types are :
Object
Type
Enum
Single Dimentional Array
bool, byte, float char, double, int, long, string .... etc.
But as it supports Type as well as strings, you can pass a Type and the name of the method to create a delegate inside the Attribute class.
public delegate object ParameterConverter(string val);
[AttributeUsage(AttributeTargets.Property)]
public class ParameterConverterAttribute : ParameterBaseAttribute
{
public ParameterConverter Converter { get; set; }
public ParameterConverterAttribute(Type delegateType, string method)
{
try{ // Important as GetMethod can throw error exception or return null
this.Converter = (ParameterConverter)Delegate.CreateDelegate(delegateType, delegateType.GetMethod(method));
}
catch { }
}
public object Convert(string val)
{
if(this.Converter != null)
return Converter(val);
}
}
And now you can use it like :
public class Tester
{
[ParameterConverter(typeof(ParameterConverter), "TestConverter"]
public int Foo{get;set;}
static object TestConverter(string val)
{
return 10;
}
}
I hope this would help you.
Lookup for TypeConverter class
or
Type Converter Example
This example shows how to create a type converter named AuthorConverter....The AuthorConverter example converts an Author object to a String and a String representation to an Author object.
UPDATE:
You can skip the limitations of attributes like #abhishek has shown.
Possible another way is to define some "convention over configuration": converter function is a method defined like so
private static Converter(string val) defined inside same class. In your case:
public class Tester
{
public int Foo{get;set;}
private static int FooConverter(string val)
{
return 10;
}
}
You can put some ParameterConverterAttribute on top of the property as a sign that custom converter function exists, but is not mandatory.

Dealing with object parameters and generics

I have a the following generic methods:
public static string Glue(string prefix, string value)
{
return String.Format("{0}={1}&", prefix, value);
}
public static string Format<T>(string prefix, T obj) where T : struct
{
return Glue(prefix, (obj).ToString()); ;
}
public static string Format<T>(string prefix, List<T> obj) where T : struct
{
return String.Join("",obj.Select(e => Glue(prefix, e.ToString())).ToArray());
}
Now I'd like to call them with a parameter that comes in as an object and could be a variety of types.
I started writing some code and it started looking like it's going to have a very long if/else sequence:
// type of value is object, and newPrefix is string
if (value is int)
{
return Format(newPrefix, (int)(value));
}
else if (value is double)
{
return Format(newPrefix, (double)value);
}
...
Is there a way of avoiding this long sequence of if/else?
As stated there's not much of a way to make this simpler. The Format method is constrained to only taking value types (structs) which is easy to detect at the call site
if (value.GetType().IsValueType) {
// it's a struct
}
But there's no way to then to make the Format call happy as you can't provide the T type.
What you can do here is change Format slightly. The method call only uses the ToString method which is available on all types. You could remove the struct constraint and then call it with the value already in object form
public static string Format(string prefix, object obj) {
return Glue(prefix, obj.ToString()); ;
}
if (value.GetType().IsValueType) {
Format(newPrefix, value);
}

How to write generic extension methods?

I am developing a generic wrapper around TryParse, as follows:
public delegate bool ParseDelegate<T>(string s, out T result);
public static T? ParseOrNull<T>(this string value, ParseDelegate<T> parse) where T : struct
{
T result;
var parsed = parse(value, out result);
return parsed ? result : (T?)null;
}
[Test]
public void ParsesValidInt()
{
Assert.AreEqual(1234, "1234".ParseOrNull<int>(int.TryParse));
}
[Test]
public void ParsesValidDecimal()
{
Assert.AreEqual(12.34M, "12.34".ParseOrNull<decimal>(decimal.TryParse));
}
This is kinda repetitive. Is there a way to avoid mentioning int.TryParse at all, so that my calls look as follows:
"1234".ParseOrNull<int>()
Is there a way to avoid mentioning int.TryParse at all, so that my calls look as follows:
Not directly, as TryParse isn't part of a shared interface. If there were a shared interface to these value types, this would be possible via a constraint.
Personally, I would not suggest using extension methods for this. I would rather write this as something more like:
public static class Parse
{
public delegate bool ParseDelegate<T>(string s, out T result);
public static T? FromString<T>(string value, ParseDelegate<T> parse) where T : struct
{
T result;
var parsed = parse(value, out result);
return parsed ? result : (T?)null;
}
public static int? ToNullableInt32(string value)
{
return FromString<int>(value, int.TryParse);
}
public static double? ToNullableDouble(string value)
{
return FromString<double>(value, double.TryParse);
}
}
This adds a bit of overhead up front, but allows you to write these very cleanly, ie:
int? first = Parse.FromString<int>("1234", int.TryParse);
int? second = Parse.ToNullableInt32("1234");
double? third = Parse.ToNullableDouble("1234");
I see little value in putting an extension method, especially on something like string (which is used everywhere), as it "pollutes" the compilation of string itself. You'll see this everywhere you use strings - basically, any time you use this namespace, you'll end up having these parse methods in your intellisense, etc. In addition, this seems more like a "utility" than something that should appear as built-in functionality of string itself, which is why I personally prefer a separate class for it.
In short no but you can add a new helper method:
public static int? ParseInt(this string value)
{
return value.ParseOrNull<int>(int.TryParse);
}
and then:
"1234".ParseInt();
Look at how Microsoft deals with several types . They provides one method for each type. Enumerable.Sum Method is a good example. If you want to simplify the calling code, you should provide the overloads for each types :
public static int? ParseOrNull<int>(this string value)
{
int result;
var parsed = int.TryParse(value, out result);
return parsed ? result : (T?)null;
}
public static long? ParseOrNull<long>(this string value)
{
long result;
var parsed = long.TryParse(value, out result);
return parsed ? result : (T?)null;
}
// same for ulong, long, uint, ushort, short, byte,
// bool, float, double, decimal. Do I forget one ?
I think it's more important to simplify calls than the method itself. In fact, there is not a huge number of types to deal with.
Yes, you can use Convert.ChangeType
public static T? ParseOrNull<T>(this string value) where T : struct, IConvertible
{
try
{
return (T)Convert.ChangeType(value, typeof(T));
}
catch (FormatException ex)
{
return null;
}
}
It wont have as good performance (use of try catch) as TryParse, but should work for all IConvertible types
The answer is a big YES. You're trying to exploit the existence of the static T.TryParse(string, out T) function on the types you're converting to, and we can do that pretty easily with a little reflection.
public static T? ParseOrNull<T>(this string str)
where T: struct, IConvertible
{
// find the TryParse method.
var parseMethod = typeof(T).GetMethod("TryParse",
// We want the public static one
BindingFlags.Public | BindingFlags.Static,
Type.DefaultBinder,
// where the arguments are (string, out T)
new[] { typeof(string), typeof(T).MakeByRefType() },
null);
if (parseMethod == null)
// You need to know this so you can parse manually
throw new InvalidOperationException(
string.Format("{0} doesn't have a TryParse(..) function!",
typeof(T).FullName));
// create the parameter list for the function call
var args = new object[] { str, default(T) };
// and then call the function.
if ( (bool)parseMethod.Invoke(null, args))
return (T?)args[1]; // if it returned true
// if it returned false
return null;
}
This is the original answer I provided, based on the idea that you need two different parse methods: One for value types and another for reference types.
public delegate bool ParseDelegate<T>(string s, out T result);
public static T? ParseOrNull<T>(this string str, ParseDelegate<T> Parse)
where T: struct
{
T result;
if (!Parse(str, out result))
return null;
return result;
}
public static T ParseOrNull<T>(this string str, ParseDelegate<T> Parse)
where T : class
{
T result;
if (!Parse(str, out result))
return null;
return result;
}
public static T? ParseOrNull<T>(this string value)
where T : struct
{
T result = default(T);
object[] parameters = new object[] { value, result };
foreach (System.Reflection.MethodInfo method in
typeof(T).GetMethods()
.Where(method => method.Name == "TryParse")
.Where(method => method.GetParameters().Length == 2) //as opposed to the 4 argument version
.Take(1) //shouldn't be needed, but just in case
)
{
method.Invoke(null, parameters);
}
return (T)parameters[1];
}
As Reed mentions, I'd rather not use an extension method of string. I'd just use Parser.Parse(string value). Easy fix though, just remove the 'this' and voila.

Categories