How do I tell the compiler that a type supports a specific interface? #1840
Replies: 25 comments
-
What's wrong with this?
|
Beta Was this translation helpful? Give feedback.
-
@theunrepentantgeek it is wrong because we have |
Beta Was this translation helpful? Give feedback.
-
@AlexRadch I don't understand; the second parameter to |
Beta Was this translation helpful? Give feedback.
-
@jnm2 The second parameter (of the overload of interest here) must be |
Beta Was this translation helpful? Give feedback.
-
Ah, similar to #1315? |
Beta Was this translation helpful? Give feedback.
-
@jnm2 Possibly, though that seems to be geared towards specializing for a list of specific types, not for all types that satisfy a constraint. |
Beta Was this translation helpful? Give feedback.
-
@AlexRadch I think the usual answer to questions of the form "The C# type system doesn't let me do this, what do I do?" is "Bypass it by using reflection". The issue is that you can't use reflection with // Note: ignore the class names, couldn't think of better ones
abstract class ClosureBase<T>
{
public static ClosureBase<T> Instance = (ClosureBase<T>)Activator.CreateInstance(
typeof(Closure<>).MakeGenericType(typeof(T)));
public abstract int IndexOf(ReadOnlySpan<T> span, T value);
}
sealed class Closure<T> : ClosureBase<T> where T : IEquatable<T>
{
public override int IndexOf(ReadOnlySpan<T> span, T value) =>
MemoryExtensions.IndexOf(span, value);
}
public static int IndexOfEquals<T>(this Span<T> span, T value)
{
if (typeof(IEquatable<T>).IsAssignableFrom(typeof(T)))
{
return ClosureBase<T>.Instance.IndexOf(span, value);
}
return 42;
} |
Beta Was this translation helpful? Give feedback.
-
If there was a way to reinterpret the I'm new to the APIs and I can't figure out how to reinterpret the span memory this way, though. It should be safe since the size and layout of the wrapper struct should be identical to the bits in the |
Beta Was this translation helpful? Give feedback.
-
Wait, think I got it. This is passing my tests: public static unsafe int IndexOfEquals<T>(this Span<T> span, T value)
{
fixed (void* pointer = &Unsafe.As<T, byte>(ref span.GetPinnableReference()))
{
return System.MemoryExtensions.IndexOf(
MemoryMarshal.CreateSpan(ref Unsafe.AsRef<EqualityWrapper<T>>(pointer), span.Length),
new EqualityWrapper<T>(value));
}
}
private readonly struct EqualityWrapper<T> : IEquatable<EqualityWrapper<T>>
{
private readonly T value;
public EqualityWrapper(T value)
{
this.value = value;
}
public bool Equals(EqualityWrapper<T> other)
{
// Whatever you want here—this is what I’d probably go for.
return EqualityComparer<T>.Default.Equals(value, other.value);
}
public override bool Equals(object obj)
{
return obj is EqualityWrapper<T> other && Equals(other);
}
public override int GetHashCode()
{
return value == null ? 0 : value.GetHashCode();
}
} |
Beta Was this translation helpful? Give feedback.
-
The above also handles searches for |
Beta Was this translation helpful? Give feedback.
-
@jnm2 Thanks for your very cool code! |
Beta Was this translation helpful? Give feedback.
-
@svick Thanks for your very cool code! I think it is more tricky code than @jnm2 code but more fast. |
Beta Was this translation helpful? Give feedback.
-
@jkotas Could you please check that my code is safe, interpreting contiguous |
Beta Was this translation helpful? Give feedback.
-
I don't think this is the case. Compare: generic versus handwritten specialized |
Beta Was this translation helpful? Give feedback.
-
@jnm2 I think that for some standard types like |
Beta Was this translation helpful? Give feedback.
-
Yes, it should work fine, but I think it is unnecessary ceremony. You can just have a simple loop (maybe unrolled) to achieve the same effect. You are not gaining much by bending backwards to call
Correct. These optimizations won't kick in for the above. |
Beta Was this translation helpful? Give feedback.
-
@AlexRadch Ah, yes I see. @jkotas Thank you! Good to know that. |
Beta Was this translation helpful? Give feedback.
-
Seem like the "reinterpret" acrobatics can be reduced using Span<Foo> s = new Foo[42];
Span<EqualityWrapper<Foo>> e = MemoryMarshal.Cast<Foo, EqualityWrapper<Foo>>(s); |
Beta Was this translation helpful? Give feedback.
-
@mikedn We need to use it with unconstrained generics. That fails at both compile time ( public static Span<TTo> Cast<TFrom, TTo>(Span<TFrom> span)
where TFrom : struct
where TTo : struct
{
if (RuntimeHelpers.IsReferenceOrContainsReferences<TFrom>())
ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(TFrom));
if (RuntimeHelpers.IsReferenceOrContainsReferences<TTo>())
ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(TTo)); |
Beta Was this translation helpful? Give feedback.
-
Actually, what is preventing the |
Beta Was this translation helpful? Give feedback.
-
Ah, right, that's unfortunate. One can anyway achieve the same thing without using |
Beta Was this translation helpful? Give feedback.
-
There should probably just be a |
Beta Was this translation helpful? Give feedback.
-
Nothing fundamental. Inlining is not the optimization dropped here. The optimization dropped here is the manual vectorization for byte, etc. |
Beta Was this translation helpful? Give feedback.
-
These are solutions to @AlexRadch's specific problem, but the general problem could be solved with #399. Each interface check could cause the code inside the succeeding branch to intersect the type ( @jkotas It would be pretty cool if it could be both inlined and vectorized. |
Beta Was this translation helpful? Give feedback.
-
I created proposal to add pattern matching for types #1839. Also my proposal mostly is duplicate of #631 proposal. |
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.
-
I want to write the IndexOf method for
Span
and typeT
without limits:public static int IndexOfEquals<T>(this Span<T> span, T value)
.You can notice that if the type
T
implements the interfaceIEquatable<T>
then for suchT
corresponding method is already exists and we just need to call it, but the compiler will not allow us to do that.Next code have incorrect casting
(T: IEquatable<T>)value
. How to make it correct?How can I call
System.MemoryExtensions.IndexOf
method in case ifT
type supportsIEquatable<T>
interface?Beta Was this translation helpful? Give feedback.
All reactions