Weird question here - something that's come up, due to the environment I'm working within.
A bit of a preface:
I am going to abuse well-known abstractions from the Taxonomic rank to describe the situation I'm stuck with - by design decisions which were not my own, and are now Live in production on high-use data processing systems. I'm working on using (consuming) an API that was designed by others at work, I have no input on, and just need to be able to write code against.
This API is actually automatically generated C# code that's been compiled, and defines a complex interfacing type system which describes 1 Top-level Interface, hundreds of Second-level interfaces, And hundreds of Third-level interfaces, with a relationship of 1:1 on the Second-level interfaces and Third-level interfaces - IE, for every second-level interface, there is exactly 1 third-level interface, which also implements the Second-level Interface, and the top-level interface explicitly.
I will describe my conundrum (roughly) using the first 3 ranks within the field of biology's Taxonomic rank system.
A Pattern I will be using here:
I%Domain%
Is kind of a generalized stub, indicating a collection of interfaces of some arbitrary Domain (IArchaea, IBacteria, IEukarya). It could be any of these three (and in reality, there are literally hundreds).I%Kingdom%
Is kind of a generalized stub (similar toI%Domain%
), which ifI%Domain%
is actually IEukarya, would contain patterns similar to IFungi, IProtista, IPlantae, IAnimalia. The metaphor here breaks down, as in reality, there is exactly 1 Interface of this third tier that directly correlates to the known interface of the second tier. However, for the purposes of promotion, this is actually unimportant - I'm merely pointing out the metaphor's inconsistency.
// Implemented, and "Cement". Our algorithm fundamentally works with
// IOrganism as the main type for everything, using reflection to
// iterate properties, due to the needs presented.
// Consider to be Tier-1 Interface.
interface IOrganism { /*...*/ }
// Implemented, and "Nebulous" (Could be IArchaea, IBacteria, IEukarya, etc...)
// Will never actually be IDomain, but will rather be one of
// IArchaea, IBacteria, IEukarya, in a more "generic" approach.
// Note: The %Domain% syntax is *is indicative of a
// "stub" for any arbitrary pre-defined Domain-like
// interfaces. See the pattern described above.
// Consider to be Tier-2 Interface, which is "inherited"
// from by exactly 1 Tier-3 Interface.
Interface I%Domain% : IOrganism { /*...*/ }
// Implemented, and "Nebulous". See above on the Pattern section,
// as well as the comment on I%Domain%.
// Note: The %Kingdom% is indicative of a "stub"
// for any arbitrary pre-defined Kingdom interfaces.
// Consider to be a Tier-3 Interface, for which exactly 1 exists which
// implements each Tier-2 Interface.
interface I%Kingdom% : I%Domain%, IOrganism { /*...*/ }
All work is being done upon the IOrganism
interface, but it is known that every input interface to the described method (below) are also I%Kingdom%
(which is also I%Domain%
).
I need a Method in C# which can receive an input IOrganism
, assume it is an I%Kingdom%
, and promote it to the up-casted I%Domain%
type, in a generalized fashion, returning it as an IOrganism
. This is conceptually similar to unboxing, but with a 3-tier system, and defined via hierarchical patterns amongst Interfaces, without particular regard to underlying object-types, only interface declarations.
// Given an IOrganism which is provided as a sub-typed
// I%Kingdom%instance , returns the input object as a
// Type-cast I%Domain%, and stored as an IOrganism.
public static IOrganism PromoteType(IOrganism organismAs%Kingdom%)
{
// Get the type of the base as the current base type.
// Is approximately typeof(I%Kingdom%), but
// Isn't actually a generic, and rather refers to
// An arbitrary instance of an I%Kingdom%-type
// of interface.
Type baseType = organismAs%Kingdom%.GetType();
// Throw exception if the type provided is not the expected type
// Note: type-checking is an abstraction,
// we need another logical statement to determine if it
// is the I%Kingdom% "generalized" interface type
// Assume the actual statement works.
if (baseType != typeof(I%Kingdom%))
{
// assume a more descriptive error string here.
string error = "Provided object was of an invalid type."
throw new InvalidArgumentException(string.Format(error));
}
// Stores the type of I%Domain%, inherantly.
// Use LinQ to retrieve the matching interited type.
// Note: The Name.Equals()-logic on "IDomain" is an abstraction
// of the real logic, we actually have another logical statement
// to determine if it is really the I%Domain%-pattern in
// a more "generalized" fashion. Assume the "real"
// variant of this statement works properly.
Type upcastTypeAsIDomain = baseType.GetInterfaces()
.FirstOrDefault(
currInterfaceType => currInterfaceType.Name.Equals("I%Domain%"));
// Note: For the third time, I%Domain% here is a logical abstraction -
// There is another statement I'm using which works,
// I'm just representing the effective statement
// Relative to the question's context.
if (upcastTypeAsIDomain != typeof(I%Domain%))
{
// A more meaningfull error message exists in reality.
string error = "Provided object didn't implement the proper I%Domain% interim type.";
throw new InvalidArgumentException(string.Format(error));
}
return /*This is the line I need help on*/;
}
My question is on that return statement, how do I perform a "generic" (not to be confused with C# Generics) type-cast on the provided IOrganism
, known to be an I%Kingdom%
order of Interface, and return it as if it were an I%Domain%
, conceptually similar to C#'s Unboxing, knowing the Type of the object cemently as IOrganism
, but then casting it as the Type of the declared type, and storing it as if it's an IOrganism
, but where the GetType() would return the corresponding I%Domain%
, and not the true underlying I%Kingdom%
?
Reflection is fine to use here - I am aware of the performance cost, but that won't be an issue in the context this is running.
I envision some mythical syntax similar to:
// Obviously not a real Compileable line of C# - this is a pattern only.
IOrganism organismAsUpcastDomain = CAST_FROM_I%Kingdom%_TO_I%Domain%;
Is there any sort of "generic"-cast (not to be confused with C# generics) which convert from 1 Type (as an interface) to another type (also as an interface), pretending the underlying object's base type is now the second type, assuming the hierarchical definitions are correct? That way when I store this organismAs%Kingdom%
within an IOrganism
, the organismAs%Kingdom%.GetType()
would return the type of I%Domain%
, instead of I%Kingdom%
, despite fundamentally still being an I%Kindom%
internally?
The context in which this program runs will not be "live", in the sense that user-requests actively force the logic to execute, but rather, will be pre-run by developers, generate a cache-file which represents the results of this processing, and then can be looked up in real-time as per request, which will be hammered hundreds of thousands, to millions of times per day. It needs to be able to process the promotion of an arbitrary interface sub-type(depth 3) to 1 layer up (depth 2) (and stored within the tier 1 interface type).
It may not even be possible to do this in C#, as I'm unsure how well the underlying .NET Framework differentiates between the fundamental interface-type as if it is the underlying object's base type, and the type it's being stored as, allowing you to "pretend" the object of type C
is really type B
, stored within Type A
, allowing you to invoke .GetType()
on an instance of A, which would return a Type equal to typeof(B)
, while really being an object of Type C
, effectively making it lie about its own heritage.
This may look similar to Covariance and Contravariance at first, but is different, due to the fact that I'm working with arbitary Interface Types that are behaviorally and hierarchically similar to I%Domain%
and I%Kingdom%
, while describing them utilizing reflection.
Thanks for even reading the post here, as it's
- Long
- Convoluted
- Abuses the term "generic" when not actually referring to true C# Generics
- Should be unnecessary, but due to the environment I'm programming within, (I'm writing a program that needs to use reflection on the declared types in a general fashion to perform work on all inputs regardless of types, following certain pre-known patterns and hierarchies, on a data structure that could literally change at any time, to perform generalized work on this abstraction).