Skip to content

Commit 683899d

Browse files
Add support for variance to IDIC (#58646)
1 parent d801ae2 commit 683899d

2 files changed

Lines changed: 40 additions & 23 deletions

File tree

src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/DynamicInterfaceCastableHelpers.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ internal static bool IsInterfaceImplemented(IDynamicInterfaceCastable castable,
3434
if (!implType.IsDefined(typeof(DynamicInterfaceCastableImplementationAttribute), inherit: false))
3535
throw new InvalidOperationException(SR.Format(SR.IDynamicInterfaceCastable_MissingImplementationAttribute, implType, nameof(DynamicInterfaceCastableImplementationAttribute)));
3636

37-
if (!implType.ImplementInterface(interfaceType))
37+
if (!implType.IsAssignableTo(interfaceType))
3838
throw new InvalidOperationException(SR.Format(SR.IDynamicInterfaceCastable_DoesNotImplementRequested, implType, interfaceType));
3939

4040
return implType;

src/tests/Interop/IDynamicInterfaceCastable/Program.cs

Lines changed: 39 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
using System;
55
using System.Collections.Generic;
6+
using System.Runtime.CompilerServices;
67
using System.Runtime.InteropServices;
78

89
using TestLibrary;
@@ -26,9 +27,9 @@ public interface ITest
2627
int CallImplemented(ImplementationToCall toCall);
2728
}
2829

29-
public interface ITestGeneric<T>
30+
public interface ITestGeneric<in T, out U>
3031
{
31-
T ReturnArg(T t);
32+
U ReturnArg(T t);
3233
}
3334

3435
public interface IDirectlyImplemented
@@ -127,18 +128,24 @@ Type ITest.GetMyType()
127128
}
128129

129130
[DynamicInterfaceCastableImplementation]
130-
public interface ITestGenericImpl<T>: ITestGeneric<T>
131+
public interface ITestGenericImpl<T, U>: ITestGeneric<T, U>
131132
{
132-
T ITestGeneric<T>.ReturnArg(T t)
133+
U ITestGeneric<T, U>.ReturnArg(T t)
133134
{
134-
return t;
135+
if (!typeof(T).IsAssignableTo(typeof(U))
136+
&& !t.GetType().IsAssignableTo(typeof(U)))
137+
{
138+
throw new Exception($"Invalid covariance conversion from {typeof(T)} or {t.GetType()} to {typeof(U)}");
139+
}
140+
141+
return Unsafe.As<T, U>(ref t);
135142
}
136143
}
137144

138145
[DynamicInterfaceCastableImplementation]
139-
public interface ITestGenericIntImpl: ITestGeneric<int>
146+
public interface ITestGenericIntImpl: ITestGeneric<int, int>
140147
{
141-
int ITestGeneric<int>.ReturnArg(int i)
148+
int ITestGeneric<int, int>.ReturnArg(int i)
142149
{
143150
return i;
144151
}
@@ -330,40 +337,50 @@ private static void ValidateGenericInterface()
330337
Console.WriteLine($"Running {nameof(ValidateGenericInterface)}");
331338

332339
object castableObj = new DynamicInterfaceCastable(new Dictionary<Type, Type> {
333-
{ typeof(ITestGeneric<int>), typeof(ITestGenericIntImpl) },
334-
{ typeof(ITestGeneric<string>), typeof(ITestGenericImpl<string>) },
340+
{ typeof(ITestGeneric<int, int>), typeof(ITestGenericIntImpl) },
341+
{ typeof(ITestGeneric<string, string>), typeof(ITestGenericImpl<string, string>) },
342+
{ typeof(ITestGeneric<string, object>), typeof(ITestGenericImpl<object, string>) },
335343
});
336344

337345
Console.WriteLine(" -- Validate cast");
338346

339-
// ITestGeneric<int> -> ITestGenericIntImpl
340-
Assert.IsTrue(castableObj is ITestGeneric<int>, $"Should be castable to {nameof(ITestGeneric<int>)} via is");
341-
Assert.IsNotNull(castableObj as ITestGeneric<int>, $"Should be castable to {nameof(ITestGeneric<int>)} via as");
342-
ITestGeneric<int> testInt = (ITestGeneric<int>)castableObj;
347+
// ITestGeneric<int, int> -> ITestGenericIntImpl
348+
Assert.IsTrue(castableObj is ITestGeneric<int, int>, $"Should be castable to {nameof(ITestGeneric<int, int>)} via is");
349+
Assert.IsNotNull(castableObj as ITestGeneric<int, int>, $"Should be castable to {nameof(ITestGeneric<int, int>)} via as");
350+
ITestGeneric<int, int> testInt = (ITestGeneric<int, int>)castableObj;
351+
352+
// ITestGeneric<string, string> -> ITestGenericImpl<string, string>
353+
Assert.IsTrue(castableObj is ITestGeneric<string, string>, $"Should be castable to {nameof(ITestGeneric<string, string>)} via is");
354+
Assert.IsNotNull(castableObj as ITestGeneric<string, string>, $"Should be castable to {nameof(ITestGeneric<string, string>)} via as");
355+
ITestGeneric<string, string> testStr = (ITestGeneric<string, string>)castableObj;
343356

344-
// ITestGeneric<string> -> ITestGenericImpl<string>
345-
Assert.IsTrue(castableObj is ITestGeneric<string>, $"Should be castable to {nameof(ITestGeneric<string>)} via is");
346-
Assert.IsNotNull(castableObj as ITestGeneric<string>, $"Should be castable to {nameof(ITestGeneric<string>)} via as");
347-
ITestGeneric<string> testStr = (ITestGeneric<string>)castableObj;
357+
// Validate Variance
358+
// ITestGeneric<string, object> -> ITestGenericImpl<object, string>
359+
Assert.IsTrue(castableObj is ITestGeneric<string, object>, $"Should be castable to {nameof(ITestGeneric<string, object>)} via is");
360+
Assert.IsNotNull(castableObj as ITestGeneric<string, object>, $"Should be castable to {nameof(ITestGeneric<string, object>)} via as");
361+
ITestGeneric<string, object> testVar = (ITestGeneric<string, object>)castableObj;
348362

349-
// ITestGeneric<bool> is not recognized
350-
Assert.IsFalse(castableObj is ITestGeneric<bool>, $"Should not be castable to {nameof(ITestGeneric<bool>)} via is");
351-
Assert.IsNull(castableObj as ITestGeneric<bool>, $"Should not be castable to {nameof(ITestGeneric<bool>)} via as");
352-
var ex = Assert.Throws<DynamicInterfaceCastableException>(() => { var _ = (ITestGeneric<bool>)castableObj; });
353-
Assert.AreEqual(string.Format(DynamicInterfaceCastableException.ErrorFormat, typeof(ITestGeneric<bool>)), ex.Message);
363+
// ITestGeneric<bool, bool> is not recognized
364+
Assert.IsFalse(castableObj is ITestGeneric<bool, bool>, $"Should not be castable to {nameof(ITestGeneric<bool, bool>)} via is");
365+
Assert.IsNull(castableObj as ITestGeneric<bool, bool>, $"Should not be castable to {nameof(ITestGeneric<bool, bool>)} via as");
366+
var ex = Assert.Throws<DynamicInterfaceCastableException>(() => { var _ = (ITestGeneric<bool, bool>)castableObj; });
367+
Assert.AreEqual(string.Format(DynamicInterfaceCastableException.ErrorFormat, typeof(ITestGeneric<bool, bool>)), ex.Message);
354368

355369
int expectedInt = 42;
356370
string expectedStr = "str";
357371

358372
Console.WriteLine(" -- Validate method call");
359373
Assert.AreEqual(expectedInt, testInt.ReturnArg(42));
360374
Assert.AreEqual(expectedStr, testStr.ReturnArg(expectedStr));
375+
Assert.AreEqual(expectedStr, testVar.ReturnArg(expectedStr));
361376

362377
Console.WriteLine(" -- Validate delegate call");
363378
Func<int, int> funcInt = new Func<int, int>(testInt.ReturnArg);
364379
Assert.AreEqual(expectedInt, funcInt(expectedInt));
365380
Func<string, string> funcStr = new Func<string, string>(testStr.ReturnArg);
366381
Assert.AreEqual(expectedStr, funcStr(expectedStr));
382+
Func<string, object> funcVar = new Func<string, object>(testVar.ReturnArg);
383+
Assert.AreEqual(expectedStr, funcVar(expectedStr));
367384
}
368385

369386
private static void ValidateOverriddenInterface()

0 commit comments

Comments
 (0)