[英]Alternative to using Marker Interfaces to test for derived generic classes?
我有一個應用程序,它使用來自公共基類的許多類型的派生類,並且一些派生類使用泛型。 應用程序通常需要遍歷派生類的集合並標識特定類的類型以進行特殊處理。
問題是如何在不知道類型的情況下以簡潔優雅的方式測試基類的特定泛型衍生物(因為它經常在項目中完成)?
'is'運算符不適用於此:
if (item is MyDerivedGenericClass<>) // Won't compile
為了解決這個問題,我正在考慮使用空標記接口來識別集合中的特殊類。 這似乎是最干凈的解決方案。
幾個筆記:
以下示例:
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);
}
}
}
有沒有人對如何解決這個問題有任何更好的想法?
謝謝。
當我需要編寫根據對象類型而不同的代碼,並且該代碼作為虛擬方法不實用時,我使用實現為返回不可變信息的虛擬屬性的標識符。 這是更干凈,並且不使用額外的實例存儲,盡管通過類型參數初始化MarkerType會很好。
public class BaseClass
{
public abstract MarkerType { get; }
}
public enum MarkerType { A, B, C }
我已經玩了一段時間來解決這個問題,而且我已經得出結論,代碼從一開始就結構很差。 如果您在BaseClass
具有基本級別實現,那么應該使用IBaseClass
通過策略模式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; }
}
BaseClass的:
/// <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
}
然后從那里派生你的課程......
Dervied Classes:
[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 */
}
}
請注意,我在導出中添加了合同名稱。 您的特殊類擴展了BaseClass
的功能,並且該功能應該分配到另一個接口。
IDerivedSpecialBaseClass:
/// <summary>
/// Provides mechanisms for working with special BaseClasses.
/// </summary>
public interface IDerivedSpecialBaseClass : IBaseClass
{
string SpecialProperty { get; }
}
此接口繼承自IBaseClass
因為它派生自同一個根。 您的具體實現與上面相同,但實現IDerivedSpecialBaseClass
而不僅僅是IBaseClass
。
DerivedSpecialBaseClass:
/// <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);
}
原始
/// <summary>
/// Acts as a base class for all generic special classes.
/// </summary>
/// <typeparam name="T"></typeparam>
public abstract class DerivedGenericBaseClass<T> : DerivedSpecialBaseClass, IDerivedSpecialBaseClass<T>
where T : struct
{
#region Implementation of IDerivedGenericBaseClass<T>
/// <summary>
/// Does stuff with things.
/// </summary>
/// <param name="thing">The thing.</param>
public abstract void DoStuffWith(T thing);
#endregion
}
具體:
[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中處理這些內容的最簡單方法是創建一個Adapter類來處理組合。
適配器類
/// <summary>
/// Adapter class for MEF Imported contracts, deriving from BaseClass.
/// </summary>
public class Classes
{
/// <summary>
/// Gets or sets the derived classes.
/// </summary>
/// <value>
/// The derived classes.
/// </value>
/// <remarks>
/// This list will be populated via MEF.
/// </remarks>
[ImportMany("DerivedClasses", typeof(BaseClass))]
private IEnumerable<IBaseClass> DerivedClasses { get; set; }
/// <summary>
/// Gets or sets the derived special classes.
/// </summary>
/// <value>
/// The derived special classes.
/// </value>
/// <remarks>
/// This list will be populated via MEF.
/// </remarks>
[ImportMany("DerivedSpecialClasses", typeof(DerivedSpecialBaseClass))]
private IEnumerable<IDerivedSpecialBaseClass> DerivedSpecialClasses { get; set; }
/// <summary>
/// Gets or sets the derived special generic classes.
/// </summary>
/// <value>
/// The derived special generic classes.
/// </value>
/// <remarks>
/// This list will be populated via MEF.
/// </remarks>
[ImportMany("DerivedSpecialGenericClasses", typeof(DerivedGenericBaseClass<>))]
private IEnumerable<IDerivedSpecialBaseClass> DerivedSpecialGenericClasses { get; set; }
/// <summary>
/// Initialises a new instance of the <see cref="Classes"/> class.
/// </summary>
public Classes()
{
// NOTE: This is a generic application catalogue, for demonstration purposes.
// It is likely you'd rather use a Directory or Assembly catalogue
// instead, to make the application more scalable, in line with the MEF
// ideology.
var catalogue = new ApplicationCatalog();
var container = new CompositionContainer(catalogue);
container.ComposeParts(this);
}
/// <summary>
/// Processes the classes.
/// </summary>
public void ProcessClasses()
{
foreach (var item in DerivedClasses)
{
DoSomethingInteresting(item);
}
foreach (var item in DerivedSpecialClasses)
{
DoSomethingInteresting(item);
}
foreach (var item in DerivedSpecialGenericClasses)
{
DoSomethingInteresting(item);
}
}
private void DoSomethingInteresting(IBaseClass specialItem)
{
Console.WriteLine("Something interesting has been done with: " + specialItem.Title);
}
}
請注意,您不再需要檢查哪個類型,因為它們已被拆分為三個單獨的列表,使用MEF合同名稱將它們過濾到正確的列表中。 您不應該在適配器中引用具體實現,因為您在運行時不知道它們是否存在。 如果他們不這樣做,您的申請將會失敗。
相反,我建議將一個ShouldProcess
標志添加到IBaseClass
接口,並僅處理設置為true的那些。
foreach (var item in DerivedSpecialGenericClasses.Where(p => p.ShouldProcess == true))
{
DoSomethingInteresting(item);
}
沿着那條線的東西。 考慮分離您的顧慮。 MEF不知道流經它的是什么,因此您的申請也不會。 讓它成為焦點,讓插件告訴您的應用程序他們需要什么。 盡可能在最低級別實施這些檢查,以便批量處理。
當您對程序流程,設計模式和可擴展架構沒有完全了解時,使用MEF可能會很復雜。 例如,這可以通過延遲加載來實現,並將處理檢查添加為ExportMetadata
。 它不會增加太多的開銷,但如果你沒有完全吞下你的插件,可能會更復雜。 當從其他可擴展性較低的合成引擎移植到MEF時,通常需要從頭開始重建插件結構; 為了使它流暢,優雅。
為什么不這樣做:
item.GetType().GetGenericTypeDefinition().Equals(typeof(MyDerivedGenericClass<>))
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.