C# 接口隐式提升类似于拆箱,使用已知的接口分层模式:

Posted

技术标签:

【中文标题】C# 接口隐式提升类似于拆箱,使用已知的接口分层模式:【英文标题】:C# Interface Implicit Promotion similar to unboxing, using a known hierarchical pattern of Interfaces: 【发布时间】:2019-09-04 00:36:22 【问题描述】:

这里有一个奇怪的问题 - 由于我工作的环境而出现的一些问题。


前言:

我将滥用分类学等级中众所周知的抽象来描述我遇到的情况 - 通过设计决策,这些决策不是我自己的,现在已经在高使用率数据处理系统上投入生产。 我正在使用(使用)其他人在工作中设计的 API,我没有任何意见,只需要能够编写代码。

这个 API 实际上是自动生成的经过编译的 C# 代码,并定义了一个复杂的接口类型系统,它描述 1 ***接口,数百个二级接口,数百个三级接口,二级接口和三级接口1:1的关系-IE,每个二级接口,有就是1个三级接口,也实现了二级接口,显式实现了***接口。

我将使用生物学分类等级系统领域中的前 3 个等级(大致)描述我的难题。


我将在这里使用的模式:

    I%Domain% 是一种通用存根,表示某个任意域(IArchaea、IBacteria、IEukarya)的接口集合。它可以是这三个中的任何一个(实际上,有数百个)。 I%Kingdom% 是一种广义的存根(类似于I%Domain%),如果I%Domain% 实际上是 IEukarya,它将包含类似于 IFungi、IProtista、IPlantae、IAnimalia 的模式。这里的比喻不成立,因为在现实中,这个第三层恰好有 1 个接口与第二层的已知接口直接相关。但是,出于宣传的目的,这实际上并不重要-我只是指出隐喻的不一致之处。
// 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% 类型,并将其返回为 @987654334 @。这在概念上类似于拆箱,但具有 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*/;


我的问题是关于那个 return 语句,我如何在提供的IOrganism 上执行“通用”(不要与 C# 泛型混淆)类型转换,已知是接口的I%Kingdom% 顺序,以及返回它,就好像它是一个I%Domain%,在概念上类似于C#的拆箱,将对象的类型牢牢地知道为IOrganism,然后将其转换为声明类型的类型,并将其存储为好像它是@987654340 @,但是 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%;

是否有任何类型的“泛型”转换(不要与 C# 泛型混淆)从 1 类型(作为接口)转换为另一种类型(也作为接口),假装底层对象的基类型现在是第二种类型,假设分层定义是正确的?这样,当我将这个organismAs%Kingdom% 存储在IOrganism 中时,organismAs%Kingdom%.GetType() 将返回I%Domain% 的类型,而不是I%Kingdom%,尽管在内部基本上仍然是I%Kindom%

该程序运行的上下文不会是“实时”的,即用户请求主动强制执行逻辑,而是由开发人员预先运行,生成一个缓存文件,该文件表示这个处理的结果,然后可以根据请求实时查找,每天将被锤击几十万到几百万次。它需要能够处理将任意接口子类型(深度 3)提升到上一层(深度 2)(并存储在第 1 层接口类型中)。

在 C# 中甚至不可能做到这一点,因为我不确定底层 .NET 框架如何区分基本接口类型(就好像它是底层对象的基类型一样)和它正在存储的类型因为,允许您“假装”类型为C 的对象实际上是类型B,存储在类型A 中,允许您在A 的实例上调用.GetType(),这将返回一个等于@ 的类型987654354@,虽然确实是 C 类型的对象,但实际上使它对自己的遗产撒谎。

这可能看起来类似于协方差和逆变,但不同的是,因为我正在使用在行为和层次上类似于I%Domain%I%Kingdom% 的任意接口类型,同时使用描述它们反射。


感谢您阅读这里的帖子,因为它是

    长 错综复杂 在实际上并非指真正的 C# 泛型时滥用术语“泛型” 应该是不必要的,但由于我正在编程的环境,(我正在编写一个程序,它需要以一般方式对声明的类型使用反射来执行所有输入,而不管类型,遵循某些预先知道的模式和层次结构,在一个可以随时改变的数据结构上,以执行这个抽象的通用工作)。

【问题讨论】:

如果你使用类型参数语法有点难以阅读,但实际上只是接口名称中的占位符 我同意。您对语法替换有什么想法吗? 我想我可以使用 I%Domain%、I%Kingdom% 等类似的符号,并在我的前提下将该符号描述为约定。 另外,我理解正确吗?是否要更改实例的type?比如,GetType() 返回的类型与原来不同? 我不想改变底层类型,我想让对象谎报类型并假装它实际上是向上转换的变体的类型。我正在使用的系统实际上已经在执行此操作 - 我正在使用一个具体对象,调用 .GetType() 返回 I 的类型而不是具体类型,并且我正在寻找操作该对象强制它返回 I 而不是 I,以用于对向上转换的变体进行反思。这就是为什么这个请求对我来说如此奇怪的部分原因,让我质疑这种方法在 C# 中的可行性。 【参考方案1】:

所以我重构了我的代码库,允许我只返回反射的接口父类型(而不是尝试执行一些不虔诚的泛型转换),使这个问题的答案成为我需要的一个有争议的点,但我'我现在也想出了一个答案,让您可以为任何其他类型的对象提供别名类型。在这一点上它几乎是人为的,但如果出于某种原因,如果您正在使用反射并希望有一个简单的机制来跟踪您当前对象在复杂继承链中被别名为哪个程度,您可以使用以下类:

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&lt;T&gt; 类可以轻松地松散耦合类的任意实例,并让它呈现任何其他类型(按照惯例可以使用允许您使用反射从反射检测到的基本类型中获取属性/方法)。

【讨论】:

以上是关于C# 接口隐式提升类似于拆箱,使用已知的接口分层模式:的主要内容,如果未能解决你的问题,请参考以下文章

Unity常见面试题大全

C#接口的隐式和显式实现

C# 使用隐式或显示实现接口的区别

C# 接口的隐式与显示实现说明

C# 接口的隐式与显示实现

C# - 接口_隐式接口实现