[英]How to call generic method of derived class from base class reference in C#
我有以下基礎 class(省略版本):
class BaseClass
{
}
我創建了以下派生類:
class DataPreparationClass<T> : BaseClass
{
}
class DataClass<T,U> : DataPreparationClass<T>
{
public virtual void Start<U>(U arg)
{}
}
class DataClassMain : DataClass<InputData,Data>
{
public override void Start(Data argument)
{
base.Start(argument);
}
}
class DataClassAux : DataClass<InputData,AuxData>
{
public override void Start(AuxData argument)
{
base.Start(argument);
}
}
我有一個List<BaseClass>
包含各種派生實例(有更多派生類型),我打算分別調用它們的Start
方法:
List<BaseClass> instances = GetAllInstance();
foreach(BaseClass instance in instances)
{
object arg = GetArgsForInstance(instance);
// instance.Start(arg); //can't call this
}
但是,由於它們的共同基礎是BaseClass
,所以我不能在沒有強制轉換的情況下調用Start
......基本上所有可能的類型,因為它們的類型在處理時是未知的。
如果我使用動態:
((dynamic)target).Start(new Data("starting")); //target is of type DataClassMain<InputData,Data>
我得到一個例外:
Microsoft.CSharp.RuntimeBinder.RuntimeBinderException:'Client.DataClass<InputData,Data>.Start(Data)' 的最佳重載方法匹配有一些無效參數'
那么我應該如何調用未知方法呢?
我會說,您的問題表明您的 model 存在多個缺陷:
也就是說,您的問題意味着您不想修復這些缺陷,也許它們超出了您的控制范圍,因此您必須忍受它:
所以,我會嘗試做的是:
class DataClassMain : DataClass<InputData,Data>
{
public override void Start(ArgumentProvider argumentProvider)
{
Data argument = argumentProvider.getArgumentAsData(argumentProvider);
base.Start(argument);
}
}
public class DataClassMain : DataClass<InputData,Data>
{
public override void Start(object arg)
{
base.Start(arg);
Data argAsData = (Data) arg;
}
}
DataClassMain
DataClassAux
public static void Main(string[] args)
{
List<BaseClass> instances = GetAllInstance();
foreach(BaseClass instance in instances)
{
object value = GetArgsForInstance(instance);
messyInvoke(instance, value);
}
}
private static void messyInvoke(BaseClass instance, object value)
{
MethodInfo method = instance.GetType().GetMethod("Start");
if (method != null)
{
ParameterInfo[] parametersInfos = method.GetParameters();
if (parametersInfos.Length == 1)
{
object[] paramArray = {value};
method.Invoke(instance, paramArray);
}
}
}
public class BaseClass{
public virtual Type GetTypeOfArgs()
{
return typeof(Toto);
}
}
public class DataPreparationClass<T> : BaseClass {}
public abstract class DataClass<T> : DataPreparationClass<T>
{
}
public class DataClassMain : DataClass<Toto>
{
public void Start(Data arg)
{
Console.WriteLine("DataClassMain");
}
}
public class DataClassAux : DataClass<Toto>
{
public void Start(AuxData argument)
{
Console.WriteLine("DataClassAux");
}
}
private static object GetArgsForInstance(BaseClass isntance)
{
if (isntance is DataClassMain)
return new Data();
if (isntance is DataClassAux)
return new AuxData();
throw new ArgumentException();
}
private static List<BaseClass> GetAllInstance()
{
return new List<BaseClass> {new DataClassMain(), new DataClassAux()};
}
public class Toto{}
public class DataClassInputData
{
}
public class Data : DataClassInputData
{
}
public class AuxData : DataClassInputData
{
}
因此,對您的問題最直接的答案是使用模式匹配來調用 start 方法。
List<BaseClass> instances = GetAllInstance();
foreach(BaseClass instance in instances)
{
object arg = GetArgsForInstance(instance);
switch(instance){
case DataClassMain d : d.Start((Data)arg); break;
case DataClassAux a : a.Start((AuxData)arg);break;
default: throw new Exception();
}
}
但我確實覺得這是一個令人費解且不合適的 inheritance 鏈,您應該真正考慮使用工廠和/或策略模式。
假設GetArgsForInstance
總是會返回關於它作為參數接收的類型的正確類型,並且返回類型(Data、AuxData 等)共享一個公共的基本類型。 所以我們可以直接使用T GetArgsForInstance<T>(BaseClass b)
的簽名來進行類型解析。 這樣,您可以確保在返回之前獲得正確類型的 args。
由於Start
覆蓋只是傳遞調用泛型類型,因此DataClassMain
和DataClassAux
中的覆蓋是不必要的。
如果我們稍微修改一下 DataClass,我們可以這樣做:
class DataClass<T,U> : DataPreparationClass<T>
{
public virtual void Start(U arg)
{
//Do somethin with arg
}
public void Call(Func<BaseClass,U> f){
U data = f.Invoke(this);
Start(data);
}
}
並調用它
List<BaseClass> instances = GetAllInstance();
foreach(BaseClass instance in instances)
{
switch(instance)
{
case DataClassMain d : d.Call(GetArgsForInstance<Data>); break;
case DataClassAux a : a.Call(GetArgsForInstance<AuxData>);break;
default: throw new Exception();
}
}
這樣做更可取的原因是我們可以讓編譯器確保我們只將適當的類型傳遞給不同的方法,而不需要強制轉換。
但同樣,幾乎總是應該避免這種復雜的 inheritance 鏈。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.