一点前言:
我将滥用分类学排名中的众所周知的抽象来描述我所困扰的情况 - 通过我自己的设计决策,并且现在在高使用率的数据处理系统中生活。我正在努力使用(使用)由其他人在工作中设计的API,我没有输入,只需要能够编写代码。
该API实际上是自动生成的C#代码,它已经被编译,并定义了一个复杂的接口类型系统,它描述了1个顶级接口,数百个二级接口和数百个第三级接口,关系为1:1 on第二级接口和第三级接口 - IE,对于每个二级接口,只有1个第三级接口,它还实现了第二级接口和显式的顶级接口。
我将使用生物学分类学系统领域中的前三个等级来描述我的难题(大致)。
我将在这里使用的模式:
I%Domain%
是一种广义存根,表示一些任意域(IArchaea,IBacteria,IEukarya)的接口集合。它可以是这三个中的任何一个(实际上,实际上有数百个)。I%Kingdom%
是一种广义存根(类似于I%Domain%
),如果I%Domain%
实际上是IEukarya,将包含类似于IFungi,IProtista,IPlantae,IAnimalia的模式。这里的比喻打破了,实际上,这个第三层中恰好有一个接口与第二层的已知接口直接相关。然而,出于促销的目的,这实际上并不重要 - 我只是指出隐喻的不一致性。// 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 { /*...*/ }
所有工作都是在IOrganism
界面上完成的,但众所周知,所描述方法的每个输入接口(下面)也是I%Kingdom%
(也是I%Domain%
)。
我需要一个C#中的方法,可以接收输入IOrganism
,假设它是一个I%Kingdom%
,并以一般化的方式将其推广到升级的I%Domain%
类型,将其作为IOrganism
返回。这在概念上类似于拆箱,但具有3层系统,并通过接口之间的分层模式定义,而不特别考虑底层对象类型,仅涉及接口声明。
// 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*/;
}
我的问题是关于返回声明,我如何在提供的IOrganism
(已知为接口的I%Kingdom%
顺序)上执行“通用”(不要与C#Generics混淆)类型转换,并将其返回,就像它是一个I%Domain%
,在概念上类似于C#的Unboxing,知道水泥对象的类型为IOrganism
,但随后将其作为声明类型的类型,并将其存储为IOrganism
,但GetType()将返回相应的I%Domain%
,而不是真正的潜在I%Kingdom%
?在这里使用反射很好 - 我知道性能成本,但这不会成为运行环境中的问题。
我设想了一些类似于以下的神话语法:
// Obviously not a real Compileable line of C# - this is a pattern only.
IOrganism organismAsUpcastDomain = CAST_FROM_I%Kingdom%_TO_I%Domain%;
是否存在任何类型的“通用”-cast(不要与C#泛型混淆),它从1 Type(作为接口)转换为另一种类型(也作为接口),假装底层对象的基类型现在是第二种类型,假设分层定义是正确的?这样当我将organismAs%Kingdom%
存储在IOrganism
中时,organismAs%Kingdom%.GetType()
将返回I%Domain%
的类型,而不是I%Kingdom%
,尽管从根本上仍然是内部的I%Kindom%
?
在用户请求主动强制逻辑执行的意义上,此程序运行的上下文将不是“实时”,而是由开发人员预先运行,生成表示此结果的缓存文件处理,然后可以根据请求实时查找,每天数百万次,数百万次。它需要能够处理任意接口子类型(深度3)到1层向上(深度2)的提升(并存储在第1层接口类型中)。
在C#中甚至不可能这样做,因为我不确定底层.NET Framework如何区分基本接口类型,就好像它是底层对象的基类型,以及它存储的类型,允许你“假装”C
类型的对象实际上是类型B
,存储在类型A
中,允许你在A的实例上调用.GetType()
,它将返回一个类型等于typeof(B)
,而实际上是类型C
的对象,有效让自己的遗产撒谎。
这可能看起来类似于协方差和逆变,但是不同,因为我正在使用行为和层次上类似于I%Domain%
和I%Kingdom%
的仲裁接口类型,同时使用反射来描述它们。
感谢甚至在这里阅读帖子,因为它是
所以我重构了我的代码库,允许我只返回反射的接口父类型(而不是尝试执行一些不合理的泛型),使得这个问题的答案对我的需求没有实际意义,但我现在也是提出一个答案,让您为任何其他类型的对象提供别名类型。此时它几乎是人为的,但如果由于某种原因,如果你正在使用反射并希望有一个简单的机制来跟踪复杂继承链中的当前对象被别名的程度,你可以使用以下类:
public abstract class TypeAlias
{
public virtual object ValueAsObject { get; set; }
public virtual Type PresentingType { get; set; }
}
public class TypeAlias<T> : TypeAlias
where T : class
{
private T underlyingTypeSafeObject;
public TypeAlias()
: base()
{
base.PresentingType = typeof(T);
}
public T TypeSafeObject
{
get
{
return this.underlyingTypeSafeObject;
}
set
{
this.underlyingTypeSafeObject = value;
if (base.PresentingType == null && value != null)
{
base.PresentingType = value.GetType();
}
}
}
public override object ValueAsObject
{
get
{
return this.underlyingTypeSafeObject;
}
set
{
// returns null if cast conversion fails - not type-safe on the TypeAlias level.
this.underlyingTypeSafeObject = value as T;
}
}
public override Type PresentingType
{
get => base.PresentingType; set => base.PresentingType = value;
}
}
使用该约定,并考虑以下接口(和实现):
public interface IOrganism
{
string Be();
}
public interface IDomain : IOrganism
{
string DomainName { get; set; }
}
public interface IKingdom : IDomain, IOrganism
{
string KingdomName { get; set; }
}
public class SeventhDimension : IDomain
{
private string domainName = "7th Dimension";
string IDomain.DomainName { get => domainName; set => domainName = value; }
public virtual string Be()
{
return string.Format("{0} Exists.", this.domainName);
}
}
public class KingdomHyrule : SeventhDimension, IKingdom, IDomain
{
private string kingdomName = "Hyrule";
string IKingdom.KingdomName { get => kingdomName; set => kingdomName = value; }
public virtual string Be()
{
string s = base.Be();
s += string.Format(" Also, {0} Exists.", this.kingdomName);
return s;
}
}
我们现在可以使用以下代码来提供不同的别名,我们可以控制它们的表示,以允许对对象的任何特定实现的继承谱系进行不同程度的反思:
// Create a Hyrule Kingdom, which is also a SeventhDomain,
// and IOrganism, IKingdom, IDomain.
IOrganism testOrganism = new KingdomHyrule();
Console.WriteLine(testOrganism.Be());
// Construct a TypeAlias, which by default presents
// the type of the container you store it as,
// using Generics.
TypeAlias alias = new TypeAlias<SeventhDimension>()
{
ValueAsObject = testOrganism
};
Console.WriteLine();
// Grab the real type of the testOrganism,
// which will be KingdomHyrule
Console.WriteLine(testOrganism.GetType().Name);
// Grab the faked type of testOrganism,
// which will be SeventhDimension, due to
// the construction of the Alias.
Console.WriteLine(alias.PresentingType.Name);
// Could be retrieved using reflection on Implementing Types
alias.PresentingType = typeof(IOrganism);
Console.WriteLine(alias.PresentingType.Name);
/* Output:
* 7th Dimension Exists. Also, Hyrule Exists. | from testOrganism.Be();
*
* KingdomHyrule | from testOrganism.GetType().Name;
* SeventhDimension | from alias.PresentingType.Name;
* IOrganism | from alias.PresentingType.Name, after assignment
* */
正如我上面所说 - 我甚至不再需要这个,但类TypeAlias和TypeAlias<T>
使得容易松散地耦合一个类的任意实例,并让它呈现任何其他类型(按照惯例可以使用它来允许你使用从反射检测到的基类型中获取属性/方法的反射)。