方法的类型约束适用于算术类型。

huangapple go评论63阅读模式
英文:

Type constraint on method for arithmetic types

问题

I've been messing around with generic methods for my own console applications, because some operations just seemed really common to me.
I'm currently wandering if there's a way to constrain a method's input type to a specific set of types in hard code by means of listing/methods that they contain, which could mean that I don't have to suppress warnings in my code if the assumption is there. Here's my current code:

static T GetNumber<T>() where T : new()
{
    Type type = typeof(T);
    MethodInfo TryParse = (from item in type.GetMethods() where item.Name == "TryParse" select item).ElementAt(0);

    while(true)
    {
        string input = NotNullInput();
        object[] parameters = new object[] { input, new T() };
        #pragma warning disable CS8605
        if (!(bool)TryParse.Invoke(new T(), parameters))
            Console.WriteLine($"Invalid {type.Name} input");
        else
            return (T)parameters[1];
        #pragma warning restore CS8605
    }
}

Just makes sure the user puts in the correct format, and I know that it would probably only ever be used on types like int, float, double, long etc. I know all the types I've thought of using contained the TryParse method, so if there's a way to assume that, it would fix everything.

Also open to suggestions on how to improve the code because I'm still learning how to mess around with types and reflection.

英文:

I've been messing around with generic methods for my own console applications, because some operations just seemed really common to me.
I'm currently wandering if there's a way to constrain a method's input type to a specific set of types in hard code by means of listing/methods that they contain, which could mean that I don't have to suppress warnings in my code if the assumption is there. Here's my current code:

static T GetNumber<T>() where T : new()
{
    Type type = typeof(T);
    MethodInfo TryParse = (from item in type.GetMethods() where item.Name == "TryParse" select item).ElementAt(0);

    while(true)
    {
        string input = NotNullInput();
        object[] parameters = new object[] { input, new T() };
        #pragma warning disable CS8605
        if (!(bool)TryParse.Invoke(new T(), parameters))
            Console.WriteLine($"Invalid {type.Name} input");
        else
            return (T)parameters[1];
        #pragma warning restore CS8605
    }
}

Just makes sure the user puts in the correct format, and I know that it would probably only ever be used on types like int, float, double, long etc. I know all the types I've thought of using contained the TryParse method, so if there's a way to assume that, it would fix everything.

Also open to suggestions on how to improve the code because I'm still learning how to mess around with types and reflection.

答案1

得分: 2

有一个内置接口表达了“这个类型有一个TryParse方法”的想法,从.NET 7开始 - IParseable<T>

您可以将T约束为该类型,它不仅适用于数字,还适用于实现IParseable<T>的任何类型。如果您只希望它适用于数字,还有子接口INumber<T>,您可以使用它来代替。

示例:

public static T ParseInputAsTypeUntilSuccessful<T>() where T: IParsable<T> {
    while (true) {
        if (T.TryParse(Console.ReadLine(), CultureInfo.InvariantCulture, out var result)) {
            return result;
        } else {
            Console.WriteLine($"无效的 {typeof(T).Name} 输入!");
        }
    }
}

请注意,您需要指定一个IFormatProvidernull,在这里我使用了CultureInfo.InvariantCulture

英文:

There is a built-in interface that expresses the idea of "this type has a TryParse method" since .NET 7 - IParseable<T>.

You can constrain T to that type, and it will work not just for numbers, but any type that implements IParseable<T>. If you only want it to work for numbers, there is also the subinterface INumber<T> that you can use instead.

Example:

public static T ParseInputAsTypeUntilSuccessful<T>() where T: IParsable<T> {
    while (true) {
        if (T.TryParse(Console.ReadLine(), CultureInfo.InvariantCulture, out var result)) {
            return result;
        } else {
            Console.WriteLine($"Invalid {typeof(T).Name} input!");
        }
    }
}

Note that you need to specify an IFormatProvider or null, and here I have used CultureInfo.InvariantCulture.

答案2

得分: 1

调用方可以传递方法本身而不是类型(类型会被推断)。

如果您这样编写方法:

public static T GetNumber<T>(Func<string, T> func)
{
    while (true)
    {
        string input = NotNullInput();
        try
        {
            return func(input);
        }
        catch
        {
            Console.WriteLine($"Invalid {typeof(T).Name} input");
        }
    }
}

然后可以像这样调用它:

var myInteger = GetNumber(int.Parse);

或者

var myDecimal = GetNumber(decimal.Parse);

甚至

byte[] myArray = GetNumber(Convert.FromBase64String);

请注意,传递解析委托会自动为您定义 T,因此您不必显式传递 T

英文:

The caller can pass the method itself instead of the type (the type gets inferred).

If you write the method this way:

public static T GetNumber&lt;T&gt;(Func&lt;string,T&gt; func)
{
	while(true)
	{
		string input = NotNullInput();
		try
		{
			return func(input);
		}
		catch
		{
			Console.WriteLine($&quot;Invalid {typeof(T).Name} input&quot;);
		}
	}
}

Then you'd call it like this:

var myInteger = GetNumber(int.Parse);

Or

var myDecimal = GetNumber(decimal.Parse);

Or even

byte[] myArray = GetNumber(Convert.FromBase64String);

Notice that passing the parsing delegate automatically defines T for you, so you don't have to pass T explicitly.

huangapple
  • 本文由 发表于 2023年6月8日 16:57:32
  • 转载请务必保留本文链接:https://go.coder-hub.com/76430200.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定