8000 allow casting objects to generic .NET interfaces without specifying g… · pythonnet/pythonnet@537ddf4 · GitHub
[go: up one dir, main page]

Skip to content
65EF

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

Lines changed: 1 addition & 0 deletions
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

Lines changed: 49 additions & 0 deletions
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)
0