簡體   English   中英

僅當在 C# 中實現時才在接口實現上調用泛型方法

[英]Call generic method on interface implementation only if implemented in C#

給定一個通用的逆變接口:

public interface DoThis<in TParam> where TParam : Param
{
    void Do(TParam param);
}

這對Param有一個約束,一個標記接口:

public interface Param { }

存在具體實現的地方:

public class TheClass : DoThis<Concrete1>, DoThis<Concrete2>
{
    public void Do(Concrete1 param) => Console.WriteLine("Called Concrete1");
    public void Do(Concrete2 param) => Console.WriteLine("Called Concrete2");
}

Param 實現只是演示了這種情況:

public class Concrete1 : Param { }

public class Concrete2 : Param { }
  
public class Concrete3 : Param { }

問題

是否可以構建一個參數列表並測試該類是否實現了特定接口並調用它,最好不要反射?

是否可以Non-reflective way using ref - call in a loop來獲得Non-reflective way using ref - call in a loop下面工作?

TheClass c = new TheClass();

Param[] @params = {new Concrete1(), new Concrete2(), new Concrete3()};

foreach (var p in @params)
{
    c.Do(p);
}

當然上面的不會編譯。

反光解決方案

我們可以看看[反射]來做到這一點......

foreach (var p in @params)
{
    MethodInfo? method = c.GetType()
        .GetMethods()
        .FirstOrDefault(
            x => x.Name == nameof(DoThis<Param>.Do)
            && x.GetParameters().Length == 1
            && x.GetParameters()[0].ParameterType == p.GetType());
                
    method?.Invoke(c, new object[] {p});
}

控制台輸出:

Called Concrete1
Called Concrete2

使用 ref 的非反射方式

這並不完全有效。

private void DoParam<TParam>(TheClass c, TParam @event) 
    where TParam : Param
{
    DoParam(ref c, ref @event);
}

private void DoParam<TParam>(ref TheClass c, ref TParam @event)
    where TParam : Param
{
    if (c is DoThis<TParam> projects)
    {
        projects.Do(@event);
    }
}

循環調用

以下循環將 Param 作為導致沒有控制台輸出的類型傳遞:

foreach (var p in @params)
{
    DoParam(c, p);
}

直接打電話

但是,這確實輸出到控制台:

DoParam(c, new Concrete1());
DoParam(c, new Concrete2());
DoParam(c, new Concrete3());

輸出:

Called Concrete1
Called Concrete2

不,如果沒有反射或其他hacky方法,這是不可能的。 但是您可以使用以下方法檢查所有類型:

            TheClass c = new TheClass();

            Param[] @params = { new Concrete1(), new Concrete2(), new Concrete3() };

            foreach (var p in @params)
            {
                if (p is Concrete1 c1 && c is DoThis<Concrete1> t1) t1.Do(c1);
                else if (p is Concrete2 c2 && c is DoThis<Concrete2> t2) t2.Do(c2);
                else if (p is Concrete3 c3 && c is DoThis<Concrete3> t3) t3.Do(c3);
            }

但是,我不建議這樣做,因為在添加新的“具體”類時,必須一次又一次地手動擴展此代碼。 這很容易被遺忘。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM