Generic "in" constraint for similar types #1371
Replies: 5 comments
-
Check out #110 |
Beta Was this translation helpful? Give feedback.
-
Note: this can be managed by the 'Shapes' proposal (#164). Using your example you would have something like this (note: syntax is tbd, so don't take this as gospel). shape CrunchShape<T> {
{
static T MinValue { get; }
static T MaxValue { get; }
static operator T *(T t1, T t2);
static implicit operator T(double d);
}
public extension DoubleCrunchShapeExt of double : CrunchShape<double> {
public static double MinValue => double.MinValue; // same for maxvalue;
public static operator double *(double t1, double t2) => t1 * t2;
public static implicit operator double(double d) => d;
}
public extension DecimalCrunchShapeExt of decimal : CrunchShape<decimal> {
public static decimal MinValue => decimal.MinValue; // same for maxvalue;
public static operator decimal *(decimal t1, decimal t2) => t1 * t2;
public static implicit operator decimal(double d) => (decimal)d;
}
public void CrunchComplexDataBillionsOfTimes<T>(T[] data) where T : CrunchShape<T> {
T minValue = T.MinValue;
T maxValue = T.MaxValue;
// if all where T constraints cannot be assigned from float type numbers, compile error
// this would require relaxing the 'm' or 'M' suffix for decimal
T num1 = 5.0;
T num2 = 6.1;
// if all where T constraints cannot be multiplied, compile error
T num3 = num1 * num2;
} Now you can call CrunchComplexData on a double array or a decimal array and, as long as the 'extension' types were in scope, it will know how to handle it. Because of how the runtime works, these 'extension' types (Which are just structs) get effectively erased and it will generate efficient code for CrunchComplexDataBillionsOfTimes when instantiated with |
Beta Was this translation helpful? Give feedback.
-
That's great. Would the framework ship with a default numeric shape I hope
:) Maybe it's in .netstandard 3.0 or whatever but it would be nice.
…-- Jeff
On Sat, Mar 10, 2018 at 4:19 PM, CyrusNajmabadi ***@***.***> wrote:
Note: this can be managed by the 'Shapes' proposal (#164
<https://github.com/dotnet/csharplang/issues/164>). Using your example
you would have something like this (note: syntax is tbd, so don't take this
as gospel).
shape CrunchShape<T> {
{
static T MinValue { get; }
static T MaxValue { get; }
static operator T *(T t1, T t2);
}
public extension DoubleCrunchShapeExt of double : CrunchShape<double> {
public static double MinValue => double.MinValue; // same for maxvalue;
public static operator double* (double t1, double t2) => t1 * t2;
}
public void CrunchComplexDataBillionsOfTimes<T>(T[] data) where T : CrunchShape<T> {
// your original code practically verbatim
T minValue = T.MinValue;
// rest of your code
}
Now you can call CrunchComplexData on a double array and, as long as
DoubleCrunchShapeExt is in scope, it will know how to handle it.
—
You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHub
<https://github.com/dotnet/csharplang/issues/1371#issuecomment-372075250>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/ABNoGJsmKeEmc1kjhER8oj1hDQA7XnAXks5tdF9mgaJpZM4Sle5a>
.
|
Beta Was this translation helpful? Give feedback.
-
That would make sense. At least for the intersection of members with the right shape shared across the core numeric types. |
Beta Was this translation helpful? Give feedback.
-
You can set the type at compile-time via |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
EDIT I see https://github.com/dotnet/corefx/issues/27167, this solves the number constraint, but I am leaving this here in case it is useful to have the "in" keyword for where T constraints for other scenarios besides numbers.
The C++ #define is very powerful, being able to replace the macro anywhere in the code with a specific value. Having something similar C# would be very handy, eliminating code duplication and potentially improving performance in some cases where reflection is likely being used, like this snippet: https://codereview.stackexchange.com/questions/26022/generic-calculator-and-generic-number.
My use case is that I had a class that was doing numeric calculations. In some cases double was needed for performance. In other cases, decimal was needed for exact precision. I had to copy and paste my entire class, replacing all the double keywords with decimal. If I had something like this, that would not have been necessary:
The new "in" keyword for where T constraints would be an indicator that T must be one of the types specified. The compiler could then ensure all method calls and operator usages in the class are valid for all the types in the "in" list.
It's possible this is mainly a "number" issue, in which case a where T : number could be used, allowing T to be one of the numeric primitives (int, long, float, double, decimal, etc.).
The "m", "M" suffix would need to be handled or relaxed for decimal types or dealt with in some way if T is not a decimal.
Please let me know if there is a good reason why this is a bad idea. I would love to have this.
Beta Was this translation helpful? Give feedback.
All reactions