Phantom generic constraints in C# - c#

I came across this problematic quite often: I like to overload some method with same parameters for different return types, but .NET refuses generic constraints to sealed classes/primitives. I'll refer to this pattern as phantom generics.
I know an ugly workaround: Putting every single interface the type implements behind the where statement.
My Question: Is there any way to use explicit types in generics to illustrate the return type and keep methods distinct?
Here is my code:
public static class Reinterpret {
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe float Cast<T>(int value) where T : float { //'float' is not a valid constraint. A type used as a constraint must be an interface, a non-sealed class or a type parameter.
return *((float*)&value); //reinterpret the bytes of 'value' to a float
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe float Cast<T>(uint value) where T : float { //'float' is not a valid constraint. A type used as a constraint must be an interface, a non-sealed class or a type parameter.
return *((float*)&value);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe double Cast<T>(long value) where T : double { //'double' is not a valid constraint. A type used as a constraint must be an interface, a non-sealed class or a type parameter.
return *((double*)&value);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe double Cast<T>(ulong value) where T : double { //'double' is not a valid constraint. A type used as a constraint must be an interface, a non-sealed class or a type parameter.
return *((double*)&value);
}
}

Here's one slightly different way of approach it:
// Constraints just to be vaguely reasonable.
public static class Reinterpret<T> where T : struct, IComparable<T>
{
public T Cast(int value) { ... }
public T Cast(uint value) { ... }
public T Cast(float value) { ... }
public T Cast(double value) { ... }
// etc
}
For the implementation, you could just have a Func<int, T> field, a Func<double, T> field etc, and then have a big static constructor:
static Reinterpret()
{
if (typeof(T) == typeof(int))
{
// Assign all the fields using lambda expressions for ints.
// The actual assignment might be tricky, however - you may
// need to resort to some ghastly casting, e.g.
castDouble = (Func<double, T>)(Delegate)(Func<double, int>)
x => *((double*)&value;
}
....
}
Then for any type you didn't want to support, the fields would be null. Each Cast method would look like:
if (castIntMethod != null)
{
return castInt(value);
}
throw new InvalidOperationException("...");
To be honest, this isn't something I'd really want to do. I'd generally just use BitConverter. But it's an option.

Generics are not templates. They do not act like templates. They cannot be made to act like templates.
A "phantom" generic parameter is not going to help you simulate templates (and reinterpret_cast is not an actual template, anyway), because you soon are going to run into the fact that generics do not support specialization.
In particular, you asked "Is there any way to use explicit types in generics to ... keep methods distinct?" and commented that "the generic constraints ... keeps [sic] the methods distinct". But they actually do not. These methods are distinct only because the argument types are different. Generics are computed from overloads, they do not influence overloading.

Related

Downcasting generic maths types

Assuming that I have a generic class/method with a generic number constraint (using the new preview generic math feature):
public void DoAThing<TValue>(TValue value) where TValue : struct, INumber<TValue>
{
...
}
Is there a way to downcast this further to one of the other interfaces? For example, one of these sorts of things:
if (typeof(IFloatingPoint<TValue>).IsAssignableFrom(typeof(TValue)))
{
// do some floating-point-only logic without a specific value
}
if (value is IFloatingPoint<TValue> fpnum)
{
// do some floating-point-only logic on fpnum
}
Currently, the compiler objects to this, saying that TValue must be convertible to IFloatingPoint<TValue> to use it as a parameter... which is exactly what I'm trying to test.

How to use generic declared variable in C#

public static object GetObject(int x)
{
return new object { };
}
public static object GetObject(string x)
{
return new object { };
}
public static void ProcessObject<T>(T x) where T : int, string <= got error here:
{
object o = GetObject(x);
}
Got error "A type used as a constraint must be an interface, a non-sealed class or a type parameter."
How can I rewrite the code to get it work without write ProcessObject(int x) and ProcessObject(string x) twice?
So what you have now (according to accepted answer and your comments) is:
public static void ProcessObject<T>(T x)
{
object o;
if (typeof(T) == typeof(int))
o = GetObject((int)(object)x);
else if (typeof(T) == typeof(string))
o = GetObject((string)(object)x);
else
throw new Exception();
// do stuff with o
}
I'd recommend making public int and string overloads, but to prevent code duplication, internally call another method:
public static void ProcessObject(int x)
{
ProcessObject(GetObject(x));
}
public static void ProcessObject(string x)
{
ProcessObject(GetObject(x));
}
private static void ProcessObject(object o)
{
// do stuff with o
}
This makes your public methods' input values clear: int and string are the only acceptable types, while still not duplicating your actual logic (// do stuff with o).
You might dislike that the two public ProcessObject methods are duplicates of each other, (on the surface anyway; under the hood, they're calling two different GetObject overloads) but I think it's the best option.
You cannot do what you are trying to do: first, it is not possible to list several classes in a generic constraint; second, the type that you can put in a constraint must be such that you could inherit it (or implement it if it is an interface). Both int and string fail this check. In cases like this, you would be better off with two separate overloads.
Just remove the where part
public static void ProcessObject<T>(T x)
{
object o = GetObject(x);
}
And also don't use object in your other methods, instead use T
it's impossible in C# take a look on Constraints on Type Parameters. Try to use dynamic
Generally speaking, if your object reacts differently based on the generic type argument, you probably shouldn't be using generics in this case. Generics are great for situations where you want to do always the same thing, no matter what the actual type used.
Therefore, generic constraints will only allow you to list one base class for a type argument. Any actual types passed to the respective type arguments are meant to be a part of the given inheritance hierarchy starting with the base class you specified, so users of your class can specify any type that matches the base class or any of its subclasses.
At the same time, you, the author of the generic class, can safely assume that the specified type has (at least) the interface of the base class indicated by the constraint. Hence, you may access any members of the base class.
If you want to allow either string or int, imagine what members that could be. Both are derived directly from System.Object, hence the restriction would make no sense as it is no restriction; every type is derived from System.Object.
Summarizing, if you really want to treat string and int differently, this is definitely a case for offering two overloads rather than one generic class.

Implicitly drop the generic argument from List<T>

TL;DR: (the title gives it away) Can I "Implicitly drop the generic argument from List<T>" in one single conversion?
A friend asked me today if it would be possible to implicitly convert a List<T> to a non-generic wrapper.
var list = new List<_some_type_>();
ObjectResult result = list;
The method would look something like this:
public static implicit operator ObjectResult(List<T> list) { ... }
Clearly, the T here is not defined, and the implicit operator's method name is the type name, ergo you can't include a generic parameter to the method name unless the type were actually generic, i.e.:
class ObjectResult<T> { ... }
Instead of
class ObjectResult { ... }
The constraints we have with user-defined conversions are (am I missing any?):
Cannot convert to or from a Base type
Cannot convert to or from an interface.
Must make conversion enclosed in one of the two types.
What makes List<T> so hard:
List<T>'s only derivation comes from Object; thus we'd have to convert directly from List<T>
List<T> has only interfaces, again, must be directly from List<T>
List<T>, obviously, is compiled up in the framework, so, it must be from our wrapper
I thought of a 2-step solution where there is a middle man we can convert from (whereas the only middle man with List<T> is Object, and due to rule #1, that's not an option).
public class ObjectResult
{
public static implicit operator ObjectResult(WrapperBase arg) { ... }
}
public class WrapperBase { }
public class ObjectResultWrapper<T> : WrapperBase
{
public static implicit operator ObjectResultWrapper<T>(List<T> arg) { ... }
}
Then, the invoking code would look like this:
var list = new List<int>();
ObjectResultWrapper<int> wrap = list;
ObjectResult result = wrap;
This doesn't solve the problem really, it's only a work around to drop T implicitly (but in two steps, not one). At this point, it'd be easier to have a helper method, and not use user-defined conversions.
There may be arguments against the goal of implicitly dropping the generic argument - I don't have anything else of why he feels this was important. Consider this simply an academic question.
The answer: No, You can't do that with an implicit cast.
Alternatives:
I think the best thing would be a static ObjectWrapper.FromList<T>(List<T>) method.
The wrapper cast is an option as well, though not nearly as elegant.
How about declaring a static "Convert" function instead of trying to declare a conversion operator? Then you could use the compiler's type inference do something like this (calling the conversion method From):
List<int> list = new List<int>();
ObjectResult result = ObjectResult.From(list);
The From method might look like this:
public class ObjectResult
{
//...
public static ObjectResult From<T>(T arg) { return new ObjectResult<T>(arg); }
//...
}
public class ObjectResult<T> : ObjectResult
{
//...
public ObjectResult(T obj) { /* ... some implementation ... */ }
//...
}

How to restrict T to value types using a constraint?

I want to restrict the possible types N can take-on using a constraint. I wish to restrict N to be either a int or a decimal.
public static Chart PopulateInto<T, N>(List<T> yAxis, List<N> xAxis) where N : int, decimal
{
// Do stuff here
}
Any help appreciated...
It's not possible to constrain a generic parameter to a specific value type.
You can however force it to be a value type or struct by adding where N : struct, but that's all.
Unfortunately, it is not possible to specify generic type constraints that only allow specific value types. More to the point, it wouldn't make much sense even if it was allowed.
You're allowed to specify a class as a generic constraint but this is because you can inherit from classes, the constraint thus sets the minimum threshold of what types you're allowed to use.
If this was allowed for value types, where you cannot inherit from those types, you would effectively limit yourself to only that type.
Thus you cannot do this, but you have a few alternatives:
You can declare it without the constraint, and handle the problem at runtime. I would not recommend this way
You can declare overloads that take the specific types you're interested in.
Since you only have two such types this is what I would recommend doing.
Here are the overloads you would declare:
public static Chart PopulateInto<T>(List<T> yAxis, List<int> xAxis)
{
// Do stuff here
}
public static Chart PopulateInto<T>(List<T> yAxis, List<decimal> xAxis)
{
// Do stuff here
}
Now, additionally, if your handling of those values doesn't really rely on the numeric quality of those types, you just want to limit which types you can handle, then you can always declare your original method as well, privately, and call this method from your overloads. This would still limit your code to only allowing int or decimal, publicly, but your implementation would still be generic. Without knowing exactly what "Do stuff here" entails it is impossible to tell if this is a viable option or not but here is the code anyway:
public static Chart PopulateInto<T>(List<T> yAxis, List<int> xAxis)
{
return PopulateInto<T, int>(yAxis, xAxis);
}
public static Chart PopulateInto<T>(List<T> yAxis, List<decimal> xAxis)
{
return PopulateInto<T, decimal>(yAxis, xAxis);
}
private static Chart PopulateInto<T, N>(List<T> yAxis, List<N> xAxis) where N : struct
{
// Do stuff here
}
There is no way to do this with a constraint. Another approach though, assuming that PopulateInto can work with a generic N, is to make the core algorihtm generic and private and offer 2 public overloads which take an int and decimal respectively. This will create a similar effect
public static Chart PopulateInto<T>(
List<T> yAxis,
List<decimal> xAxis) {
return PopulateIntoCore(yAxis, xAxis);
}
public static Chart PopulateInto<T>(
List<T> yAxis,
List<int> xAxis) {
return PopulateIntoCore(yAxis, xAxis);
}
private static Chart PopulateIntoCore<T, N>(
List<T> yAxis,
List<N> xAxis) where N : struct {
...
}
To answer the question in the Title but not the body of the question.
To cover all types commonly meant by Value Types (which includes Nullable Value Types, and also string even though it's technically a Reference type), you need 3 overloads:
public void Foo<T>(T arg) where T : struct
public void Foo<T?>(T? arg) where T : struct
public void Foo<string>(string arg)
From the MSDN Docs on generic constraints:
where T : struct The type argument must be a non-nullable value type.
As Pieter said, you cannot use a compile-time check to to this. However, you can do the following at runtime:
if(!(typeof(N).equals(typeof(int32))) && !(typeof(N).equals(typeof(decimal))))
// do something
The closet you can get is Where T: struct, IComparable, IFormattable, IConvertible.
All value types implements these interfaces.

Can a type be a reference type and a value type at the same time?

If not and the set of reference types and value types are mutually exclusive, why doesn't this compile:
public static void Do<T>(T obj) where T : struct { }
public static void Do<T>(T obj) where T : class { }
The compiler states: "Type already defines a member called 'Do' with the same parameter types.", but T and T are not the same here. One is constrained to structs, the other is constraint to classes. A call to the function should always be resolvable. Are there counter examples?
The generic constraints are not being taken as part of the overload match. It is the same as return type.
For example, this will lead to the same error (overloads differ only in return type):
public static int Do<T>(T obj) { }
public static bool Do<T>(T obj) { }
In both of these cases, the rules for matching an overload take into account only the parameters types, ignoring additional information such as constraints and return type.
No, types can never be both. The code fails because generic parameters (the <T>, that is, not the T obj) have no "overloading" concept. Nor is there anything resembling C++ template specialisation.

Categories