繁体   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