pythonnet / pythonnet/pythonnet
.NET objects returned as interface 'lose' their most-derived type identity
Nobody has claimed this yet.
- Dominant language
- C#
- Stars
- 5.5k
- Forks
- 780
- PR merge metrics
- No merged PRs in 30d
Description
Environment
- Pythonnet version: 3.0.1
- Python version: 3.10.11
- Operating System: Windows 11
- .NET Runtime: .NET Framework 4.8.9167.0
Details
-
Describe what you were trying to get done.
Return an interface derived from the one declared as a method or property's return type and be able to do RTTI to detect the type of derived interface returned. Do the same with a heterogeneous collection where the collection's type is declared as a collection of BaseInterface but the members are implementations of DerivedInterfaces. -
What commands did you run to trigger this issue?
Given some C# library code:
public interface IBaseInterface { }
public interface IDerivedInterfaceX : IBaseInterface { }
public interface IDerivedInterfaceY : IBaseInterface { }
public class ConcreteX : IDerivedInterfaceX { }
public class ConcreteY : IDerivedInterfaceY { }
public class Factory
{
public IBaseInterface PolymorphicObject => new ConcreteX();
public IEnumerable<IBaseInterface> HeterogenousPolymorphicCollection=> new IBaseInterface[] { new ConcreteY(), new ConcreteX() };
}
Python client code behaves differently than C# client code:
static void Main(string[] args)
{
Factory f = new();
IBaseInterface obj = f.PolymorphicObject;
bool isDerived = obj is IDerivedInterfaceX; // true
foreach (IBaseInterface obj2 in f.HeterogenousPolymorphicCollection)
{
bool isDerivedX = obj2 is IDerivedInterfaceX; // false then true
bool isDerivedY = obj2 is IDerivedInterfaceY; // true then false
}
}
f = Factory()
obj = f.PolymorphicObject # Type is IBaseInterface FOREVER
isDerived = isinstance(obj, IDerivedInterfaceX) # Always False
isSubclass = issubclass(type(obj), IDerivedInterfaceX) # Always False
for obj2 in f.HeterogenousPolymorphicCollection: # Type is IBaseInterface FOREVER
isDerivedX = isinstance(obj2, IDerivedInterfaceX) # ALWAYS FALSE
isDerivedY = isinstance(obj2, IDerivedInterfaceY) # ALWAYS FALSE
We know it's possible to "cheat" by testing obj.__implementation__'s type but we'd rather keep everything as interfacey as possible :)
We know the official solution is to attempt to upcast the returned objects but of course this will throw an exception if it's not possible -- we'd like to examine the type returned to ensure no exception before we upcast.
Is there a nice, pretty solution?
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with the C# Factory example and the Python client access to PolymorphicObject and HeterogenousPolymorphicCollection, comparing the exposed interface type with each object's implementation. Investigate the wrapping and type-identity behavior shown by isinstance and issubclass; done means derived-interface checks reflect the concrete returned objects without requiring callers to inspect implementation or attempt a failing upcast.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- csharp, python
- Domain
- backend, developer-experience
- Issue type
- Bug
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 30/100