C# 使用标记接口测试派生泛型类的替代方法?

C# 使用标记接口测试派生泛型类的替代方法?,c#,generics,interface,marker-interfaces,C#,Generics,Interface,Marker Interfaces,我有一个应用程序,它使用来自公共基类的许多类型的派生类,并且一些派生类使用泛型。应用程序通常需要遍历派生类的集合,并标识特定的类类型以进行特殊处理 问题是如何在不知道基类类型的情况下,以简洁而优雅的方式测试基类的特定泛型派生(因为它经常在项目中进行) “is”运算符不适用于以下情况: if (item is MyDerivedGenericClass<>) // Won't compile if(项为MyDerivedGenericClass)//将不编译 为了解决这个问题,我

我有一个应用程序,它使用来自公共基类的许多类型的派生类,并且一些派生类使用泛型。应用程序通常需要遍历派生类的集合,并标识特定的类类型以进行特殊处理

问题是如何在不知道基类类型的情况下,以简洁而优雅的方式测试基类的特定泛型派生(因为它经常在项目中进行)

“is”运算符不适用于以下情况:

if (item is MyDerivedGenericClass<>)  // Won't compile
if(项为MyDerivedGenericClass)//将不编译
为了解决这个问题,我正在考虑使用空标记接口来标识集合中的特殊类。这似乎是最干净的解决方案

几点注意:

  • 这是一个封闭的项目,不会是一个商业发行的图书馆
  • 由标记接口标记的类被标记为永久唯一类型,并且来自该类型的所有派生类都将正确继承该标记
下面是一个例子:

public interface MarkerA { }  // Empty Interface
public interface MarkerB { }  // Empty Interface
public interface MarkerC { }  // Empty Interface

public class BaseClass { }

public class DerivedClassA : BaseClass { }
public class DerivedClassB : BaseClass { }
public class DerivedClassC : BaseClass { }

public class DerivedSpecialClass : BaseClass { }

public class DerivedSpecialA : DerivedSpecialClass { }
public class DerivedSpecialB : DerivedSpecialClass { }
public class DerivedSpecialC : DerivedSpecialClass { }

public class DerivedSpecialGenericA<T> : DerivedSpecialClass, MarkerA { }
public class DerivedSpecialGenericB<T> : DerivedSpecialClass, MarkerB { }
public class DerivedSpecialGenericC<T> : DerivedSpecialClass, MarkerC { }


public void ProcessClasses(List<BaseClass> list)
    {
        // Iterate through a list of mixed classes that all derive from BaseClass
        foreach (BaseClass item in list)
        {

            // Ideal approach, but doesn't compile:

            // Try to isolate select class types to do something interesting with
            if ((item is DerivedSpecialA) || (item is DerivedSpecialB) || (item is DerivedSpecialGenericB<>))
            {
                var specialItem = item as DerivedSpecialClass;

                DoSomethingInteresting(specialItem);

                Console.WriteLine(specialItem.Title);
            }


            // Alternative workaround that tests for the Marker instead

            // Try to isolate select class types to do something interesting with
            if ((item is DerivedSpecialA) || (item is DerivedSpecialB ) || (item is MarkerB))
            {
                var specialItem = item as DerivedSpecialClass;

                DoSomethingInteresting(specialItem);

                Console.WriteLine(specialItem.Title);
            }
        }
    }
公共接口标记{}//空接口
公共接口MarkerB{}//空接口
公共接口MarkerC{}//空接口
公共类基类{}
公共类DerivedClassA:基类{}
公共类DerivedClassB:基类{}
公共类DerivedClassC:基类{}
公共类派生的特殊类:基类{}
公共类DerivedSpecialA:DerivedSpecialClass{}
公共类DerivedSpecialLB:DerivedSpecialClass{}
公共类DerivedSpecialC:DerivedSpecialClass{}
公共类DerivedSpecialGenericA:DerivedSpecialClass,MarkerA{}
公共类DerivedSpecialGenericB:DerivedSpecialClass,MarkerB{}
公共类DerivedSpecialGenericC:DerivedSpecialClass,MarkerC{}
公共void进程类(列表)
{
//遍历所有派生自基类的混合类列表
foreach(列表中的基类项)
{
//理想的方法,但不编译:
//尝试隔离选定的类类型,以便对其进行有趣的操作
如果((项目为衍生专用项)| |(项目为衍生专用项)| |(项目为衍生专用项通用项))
{
var specialItem=作为派生specialclass的项目;
DoSomethingInteresting(specialItem);
控制台写入线(专用标题);
}
//替代方法是测试标记
//尝试隔离选定的类类型,以便对其进行有趣的操作
如果((项目为DerivedSpecialA)| |(项目为DerivedSpecialB)| |(项目为MarkerB))
{
var specialItem=作为派生specialclass的项目;
DoSomethingInteresting(specialItem);
控制台写入线(专用标题);
}
}
}
有没有人对如何解决这个问题有更好的想法?


谢谢。

当我需要编写根据对象类型不同而不同的代码,并且该代码作为虚拟方法不实用时,我会使用一个实现为虚拟属性的标识符来返回不可变的信息。这更干净,并且不使用额外的实例存储,尽管通过类型参数初始化MarkerType会更好

public class BaseClass 
{
    public abstract MarkerType { get; }  
}

public enum MarkerType { A, B, C }

我一直在研究这个问题有一段时间了,我的结论是代码从一开始就是糟糕的结构。如果在
BaseClass
中有基本级别的实现,那么应该使用
IBaseClass
通过策略模式来实现。这样,每件事都有一个标准化的解决方法

在您的示例中,您将
.Title
作为
specialItem
的属性。我假设这对于所有MEF导出类都是常见的。因此,这应该在
IBaseClass
中进行标准化。然后,您可以使用合同名称或通过向导出添加
ExportMetaData
属性并使用延迟加载来分离合同

IBaseClass:

/// <summary>
///     Provides mechanisms for working with BaseClasses.
/// </summary>
public interface IBaseClass
{
    /// <summary>
    ///     Gets the title of the MEF Exported class.
    /// </summary>
    /// <value>
    ///     The title.
    /// </value>
    string Title { get; }
}
/// <summary>
///     Acts as a base implementation for all derived classes.
/// </summary>
public abstract class BaseClass : IBaseClass {

    /// <summary>
    ///     Initialises a new instance of the <see cref="BaseClass"/> class.
    /// </summary>
    /// <param name="title">The title.</param>
    protected BaseClass(string title)
    {
        // Set the title.
        Title = title;
    }

    #region Implementation of IBaseClass

    /// <summary>
    ///     Gets the title of the MEF Exported class.
    /// </summary>
    /// <value>
    ///     The title.
    /// </value>
    public string Title { get; private set; }

    #endregion
}
[Export("DerivedClasses", typeof(BaseClass))]
public class DerivedClassA : BaseClass 
{
    public DerivedClassA()
        : this("DerivedClassA")
    {
        /* IoC Friendly Constructor */
    }

    /// <summary>
    ///     Initialises a new instance of the <see cref="BaseClass"/> class.
    /// </summary>
    /// <param name="title">The title.</param>
    public DerivedClassA(string title) : base(title)
    {
        /* Construction Logic */
    }
}
/// <summary>
///     Provides mechanisms for working with special BaseClasses.
/// </summary>
public interface IDerivedSpecialBaseClass : IBaseClass
{
    string SpecialProperty { get; }
}
/// <summary>
///     Acts as a base implementation for all derived special classes.
/// </summary>
public abstract class DerivedSpecialBaseClass : BaseClass 
{
    protected DerivedSpecialBaseClass()
        : this("DerivedSpecialBaseClass")
    {
        /* IoC Friendly Constructor */
    }

    /// <summary>
    ///     Initialises a new instance of the <see cref="BaseClass"/> class.
    /// </summary>
    /// <param name="title">The title.</param>
    protected DerivedSpecialBaseClass(string title) : base(title)
    {
        /* Constructor Logic */
    }

    #region Implementation of IDerivedSpecialBaseClass

    /// <summary>
    ///     Gets the special property.
    /// </summary>
    /// <value>
    ///     The special property.
    /// </value>
    public abstract string SpecialProperty { get; }

    #endregion
}
[Export("DerivedSpecialClasses", typeof(DerivedSpecialBaseClass))]
public class DerivedSpecialA : DerivedSpecialBaseClass
{
    /// <summary>
    ///     Initialises a new instance of the <see cref="DerivedSpecialA"/> class.
    /// </summary>
    public DerivedSpecialA()
        : this("DerivedSpecialA")
    {
        /* IoC Friendly Constructor */
    }

    /// <summary>
    ///     Initialises a new instance of the <see cref="DerivedSpecialA"/> class.
    /// </summary>
    /// <param name="title">The title.</param>
    public DerivedSpecialA(string title)
        : base(title)
    {
        /* Construction Logic */
    }

    #region Implementation of IDerivedSpecialBaseClass

    /// <summary>
    ///     Gets the special property.
    /// </summary>
    /// <value>
    ///     The special property.
    /// </value>
    public override string SpecialProperty
    {
        get { return @"Hello, from DerivedSpecialA"; }
    }

    #endregion
}
/// <summary>
///     Provides mechanisms for doing stuff with things.
/// </summary>
/// <typeparam name="T">The type of things to do stuff with.</typeparam>
public interface IDerivedSpecialBaseClass<T> : IDerivedSpecialBaseClass 
    where T : struct
{
    /// <summary>
    ///     Does stuff with things.
    /// </summary>
    /// <param name="thing">The thing.</param>
    void DoStuffWith(T thing);
}
[Export("DerivedSpecialGenericClasses", typeof(DerivedGenericBaseClass<>))]
public class DerivedSpecialGenericA<T> : DerivedGenericBaseClass<T>
    where T : struct
{
    #region Overrides of DerivedSpecialBaseClass

    /// <summary>
    ///     Does stuff and things.
    /// </summary>
    /// <param name="thing">The thing.</param>
    public override void DoStuffWith(T thing)
    {
        Console.WriteLine("Doing Stuff and Things with " + thing.GetType().Name);
    }

    /// <summary>
    ///     Gets the special property.
    /// </summary>
    /// <value>
    ///     The special property.
    /// </value>
    public override string SpecialProperty
    {
        get { return @"Hello, from DerivedSpecialGenericA"; }
    }

    #endregion
}
此接口继承自
IBaseClass
,因为它派生自同一根。您的具体实现与上面相同,但实现的是
IDerivedSpecialBaseClass
,而不仅仅是
IBaseClass

DerivedSpecialBaseClass:

/// <summary>
///     Provides mechanisms for working with BaseClasses.
/// </summary>
public interface IBaseClass
{
    /// <summary>
    ///     Gets the title of the MEF Exported class.
    /// </summary>
    /// <value>
    ///     The title.
    /// </value>
    string Title { get; }
}
/// <summary>
///     Acts as a base implementation for all derived classes.
/// </summary>
public abstract class BaseClass : IBaseClass {

    /// <summary>
    ///     Initialises a new instance of the <see cref="BaseClass"/> class.
    /// </summary>
    /// <param name="title">The title.</param>
    protected BaseClass(string title)
    {
        // Set the title.
        Title = title;
    }

    #region Implementation of IBaseClass

    /// <summary>
    ///     Gets the title of the MEF Exported class.
    /// </summary>
    /// <value>
    ///     The title.
    /// </value>
    public string Title { get; private set; }

    #endregion
}
[Export("DerivedClasses", typeof(BaseClass))]
public class DerivedClassA : BaseClass 
{
    public DerivedClassA()
        : this("DerivedClassA")
    {
        /* IoC Friendly Constructor */
    }

    /// <summary>
    ///     Initialises a new instance of the <see cref="BaseClass"/> class.
    /// </summary>
    /// <param name="title">The title.</param>
    public DerivedClassA(string title) : base(title)
    {
        /* Construction Logic */
    }
}
/// <summary>
///     Provides mechanisms for working with special BaseClasses.
/// </summary>
public interface IDerivedSpecialBaseClass : IBaseClass
{
    string SpecialProperty { get; }
}
/// <summary>
///     Acts as a base implementation for all derived special classes.
/// </summary>
public abstract class DerivedSpecialBaseClass : BaseClass 
{
    protected DerivedSpecialBaseClass()
        : this("DerivedSpecialBaseClass")
    {
        /* IoC Friendly Constructor */
    }

    /// <summary>
    ///     Initialises a new instance of the <see cref="BaseClass"/> class.
    /// </summary>
    /// <param name="title">The title.</param>
    protected DerivedSpecialBaseClass(string title) : base(title)
    {
        /* Constructor Logic */
    }

    #region Implementation of IDerivedSpecialBaseClass

    /// <summary>
    ///     Gets the special property.
    /// </summary>
    /// <value>
    ///     The special property.
    /// </value>
    public abstract string SpecialProperty { get; }

    #endregion
}
[Export("DerivedSpecialClasses", typeof(DerivedSpecialBaseClass))]
public class DerivedSpecialA : DerivedSpecialBaseClass
{
    /// <summary>
    ///     Initialises a new instance of the <see cref="DerivedSpecialA"/> class.
    /// </summary>
    public DerivedSpecialA()
        : this("DerivedSpecialA")
    {
        /* IoC Friendly Constructor */
    }

    /// <summary>
    ///     Initialises a new instance of the <see cref="DerivedSpecialA"/> class.
    /// </summary>
    /// <param name="title">The title.</param>
    public DerivedSpecialA(string title)
        : base(title)
    {
        /* Construction Logic */
    }

    #region Implementation of IDerivedSpecialBaseClass

    /// <summary>
    ///     Gets the special property.
    /// </summary>
    /// <value>
    ///     The special property.
    /// </value>
    public override string SpecialProperty
    {
        get { return @"Hello, from DerivedSpecialA"; }
    }

    #endregion
}
/// <summary>
///     Provides mechanisms for doing stuff with things.
/// </summary>
/// <typeparam name="T">The type of things to do stuff with.</typeparam>
public interface IDerivedSpecialBaseClass<T> : IDerivedSpecialBaseClass 
    where T : struct
{
    /// <summary>
    ///     Does stuff with things.
    /// </summary>
    /// <param name="thing">The thing.</param>
    void DoStuffWith(T thing);
}
[Export("DerivedSpecialGenericClasses", typeof(DerivedGenericBaseClass<>))]
public class DerivedSpecialGenericA<T> : DerivedGenericBaseClass<T>
    where T : struct
{
    #region Overrides of DerivedSpecialBaseClass

    /// <summary>
    ///     Does stuff and things.
    /// </summary>
    /// <param name="thing">The thing.</param>
    public override void DoStuffWith(T thing)
    {
        Console.WriteLine("Doing Stuff and Things with " + thing.GetType().Name);
    }

    /// <summary>
    ///     Gets the special property.
    /// </summary>
    /// <value>
    ///     The special property.
    /// </value>
    public override string SpecialProperty
    {
        get { return @"Hello, from DerivedSpecialGenericA"; }
    }

    #endregion
}
沿着这些路线的东西。考虑把你的顾虑分开。MEF不知道流经它的是什么,所以您的应用程序也不知道。让这成为重点,让插件告诉你的应用程序他们需要什么。在尽可能低的级别执行这些检查,以便可以批量处理这些检查

当您不完全了解程序流、设计模式和可伸缩体系结构时,使用MEF可能会很复杂。例如,这可以通过延迟加载来实现,而将处理检查添加为
ExportMetadata
。这不会增加太多的开销,但如果你不是一次就完全吞下你的插件,它可能会更复杂。当从其他可伸缩性较差的合成引擎移植到MEF时,通常需要从头开始重建插件结构;为了使它流畅、优雅。

为什么不这样做:

item.GetType().GetGenericTypeDefinition().Equals(typeof(MyDerivedGenericClass<>))
item.GetType().GetGenericTypeDefinition().Equals(typeof(MyDerivedGenericClass))

AntiPattern:“派生类的集合,并标识特殊处理的特定类类型”。为什么这些类本身不实现一个公共接口中定义的
过程
方法?@spender-不用解释太多,这些类与一个分层节点框架相关,其中有许多类型的节点,每种类型的节点派生自一个基节点。某些节点具有泛型类型。部分