Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 537ddf4

Browse files
committed
allow casting objects to generic .NET interfaces without specifying generic arguments as long as there is no ambiguity
1 parent f48d7a9 commit 537ddf4

File tree

2 files changed

+50
-0
lines changed

2 files changed

+50
-0
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ This document follows the conventions laid out in [Keep a CHANGELOG][].
2121
- `__name__` and `__signature__` to reflected .NET methods
2222
- .NET collection types now implement standard Python collection interfaces from `collections.abc`.
2323
See [Mixins/collections.py](src/runtime/Mixins/collections.py).
24+
- you can cast objects to generic .NET interfaces without specifying generic arguments as long as there is no ambiguity.
2425
- .NET arrays implement Python buffer protocol
2526
- Python integer interoperability with `System.Numerics.BigInteger`
2627
- Python.NET will correctly resolve .NET methods, that accept `PyList`, `PyInt`,

src/runtime/Types/GenericType.cs

+49
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using System.Linq;
23

34
namespace Python.Runtime
45
{
@@ -20,10 +21,58 @@ internal GenericType(Type tp) : base(tp)
2021
/// </summary>
2122
public static NewReference tp_new(BorrowedReference tp, BorrowedReference args, BorrowedReference kw)
2223
{
24+
var self = (GenericType)GetManagedObject(tp)!;
25+
if (!self.type.Valid)
26+
{
27+
return Exceptions.RaiseTypeError(self.type.DeletedMessage);
28+
}
29+
var type = self.type.Value;
30+
31+
if (type.IsInterface && !type.IsConstructedGenericType)
32+
{
33+
var nargs = Runtime.PyTuple_Size(args);
34+
if (nargs == 1)
35+
{
36+
var instance = Runtime.PyTuple_GetItem(args, 0);
37+
return AsGenericInterface(instance, type);
38+
}
39+
}
40+
2341
Exceptions.SetError(Exceptions.TypeError, "cannot instantiate an open generic type");
42+
2443
return default;
2544
}
2645

46+
static NewReference AsGenericInterface(BorrowedReference instance, Type targetType)
47+
{
48+
if (GetManagedObject(instance) is not CLRObject obj)
49+
{
50+
return Exceptions.RaiseTypeError("only .NET objects can be cast to .NET interfaces");
51+
}
52+
53+
Type[] supportedInterfaces = obj.inst.GetType().GetInterfaces();
54+
Type[] constructedInterfaces = supportedInterfaces
55+
.Where(i => i.IsConstructedGenericType && i.GetGenericTypeDefinition() == targetType)
56+
.ToArray();
57+
58+
if (constructedInterfaces.Length == 1)
59+
{
60+
BorrowedReference pythonic = ClassManager.GetClass(constructedInterfaces[0]);
61+
using var args = Runtime.PyTuple_New(1);
62+
Runtime.PyTuple_SetItem(args.Borrow(), 0, instance);
63+
return Runtime.PyObject_CallObject(pythonic, args.Borrow());
64+
}
65+
66+
if (constructedInterfaces.Length > 1)
67+
{
68+
string interfaces = string.Join(", ", constructedInterfaces.Select(TypeManager.GetPythonTypeName));
69+
return Exceptions.RaiseTypeError("Ambiguous cast to .NET interface. "
70+
+ $"Object implements: {interfaces}");
71+
}
72+
73+
return Exceptions.RaiseTypeError("object does not implement "
74+
+ TypeManager.GetPythonTypeName(targetType));
75+
}
2776

2877
/// <summary>
2978
/// Implements __call__ for reflected generic types.

0 commit comments

Comments
 (0)