【问题标题】:Generic method check parameter in runtime运行时的通用方法检查参数
【发布时间】:2020-03-12 17:36:45
【问题描述】:
public override void Test<T> T input)
{
  if(input is TypeA)
   {
    var expected =input as TypeA;
     foreach(var a in expected.Values)
     {
       // long process
     }

    }


    else
    {
     var expected =input as TypeB;
     foreach(var a in expected.Values)
     {
       //long process
     }
    }

如何声明expected,这样我就不必编写两次foreach 语句了? TypeA 和 TypeB 没有任何关系。

【问题讨论】:

  • “漫长的过程”是一样的吗?两个as的类型一样吗?你能使TypeATypeB相关吗?
  • foreach 中的所有内容都是一样的。这些类型是通过 XSD 创建的,并且在反序列化时以任何方式关联它们都会产生问题。
  • 创建一个公开Values 的接口,并使TypeATypeB 都实现该接口...然后您可以对方法使用约束(where T: IHasValues)。否则,静态类型的 C# 没有鸭子类型...如果 TypeATypeB 不相关,唯一可行的方法是使用 dynamic 并让运行时找到 Values 属性
  • " 这些类型是通过 XSD 创建的,并且在反序列化时以任何方式关联它们都会产生问题。"你如何序列化它?你可以有一个公共的父类,如果你在子类上写适当的属性,它将被正确序列化。
  • here is an example 的如何用继承解决序列化

标签: c# generics


【解决方案1】:

鉴于 TypeATypeB 实现的不是通用类型,您可以这样做:

public void Test<T>(T input)
{
    var expectedA = input as TypeA;
    if (expectedA != null)
    {
        Process(expectedA.Values);
    }

    var expectedB = input as TypeB;
    if (expectedB != null)
    {
        Process(expectedB.Values);
    }
}

private void Process(IEnumerable<Something> values)
{
    // long process
}

或者使用模式匹配:

public void Test<T>(T input)
{
    if (input is TypeA expectedA)
    {
        Process(expectedA.Values);
    }

    if (input is TypeB expectedB)
    {
        Process(expectedB.Values);
    }
}

但如果我们不检查类型会更好。这意味着input 几乎可以是任何东西,也许不是TypeATypeB。一个方法最好只关心参数的 declared 类型。因此,如果您创建了这样的界面:

public interface IHasWhateverTheseValuesAre
{
    IEnumerable<Something> Values { get; set; }
}

TypeATypeB 都可以实现它。那么您的方法将如下所示:

public void Test<T>(T input) where T : IHasWhateverTheseValuesAre
{
    foreach (var value in input.Values)
    {
        // whatever
    }
}

该方法不关心输入是TypeATypeB还是其他类型,所以不需要检查。只有当你传递了实现 IHasWhateverTheseValuesAre 的东西时它才会编译,所以参数将始终具有 Values 属性。


你提到了

这些类型是通过 XSD 创建的,并且在反序列化时以任何方式关联它们都会产生问题。

那应该没关系。如果你有一个 TypeATypeB 的对象,这意味着它已经被反序列化了。让这两个类实现一个通用接口(根本不会改变它们)不会影响这一点。


另一种看待它的方式:

如果此方法对一组值进行操作,它是否应该将TypeATypeB 作为参数?

也许它应该看起来像这样:

public void Test(IEnumerable<Something> values)
{
    foreach (var value in values)
    {
        // whatever
    }
}

【讨论】:

    【解决方案2】:

    一种解决方案是只写一个方法(假设两个as 不是同一类型):

    private static void MyForEachLoop<T>(IEnumerable<T> enumerable) {
        foreach (var a in enumerable) {
            // long process
        }
    }
    

    或者,如果as 属于同一类型,则不需要泛型:

    private static void MyForEachLoop(IEnumerable<CommonTypeOfTheAs> enumerable) {
        foreach (var a in enumerable) {
            // long process
        }
    }
    

    这样称呼它:

    if(input is TypeA)
    {
        var expected = input as TypeA;
        MyForEachLoop(expected.Values);
    }
    else
    {
        var expected = input as TypeB;
        MyForEachLoop(expected.Values);
    }
    

    【讨论】:

      【解决方案3】:

      你可以使用类型 动态

      public override void Test(object input)
      {
       dynamic expected = input;
       if(input is TypeA)
       {
       foreach(var a in (expected as TypeA).Values)
       {
         // long process
       }
      
      }
      
      else
      {
       foreach(var a in (expected as TypeB).Values)
       {
         //long process
       }
      }
      

      你可以让它更通用。

      switch (expected.GetType())
      {
        case "TestA":
        //do something
        break;
      
        case "TestA":
        //do something
        break;
      
        default:
        throw new Exception("unexcepted Type!");
       }
      

      如果您确定类型,您可以创建一个实现值列表的接口,然后将该接口作为参数传递,但它不是通用的,每次您应该更改类的实现并且您必须确保您拥有相同的属性。

      希望对你有帮助!

      【讨论】:

      • 我不推荐dynamic。如果您要使用此处所示的dynamic,您不妨删除通用的T 参数并将类型更改为object。在这种情况下,这与使用 dynamic 相同。问题是你可以将任何类型传递给这个方法,它会编译。我们应该使用编译器来防止错误的代码。编写代码,使其仅在我们将正确的内容传递给它时才能编译。这样,如果错误,我们会得到一个编译器错误(快速、易于查找和修复)与一个运行时错误(较慢的反馈,可能更难找到。)
      • @ScottHannen 感谢您的提示!刚刚更新了我的答案。
      猜你喜欢
      • 1970-01-01
      • 2014-05-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-02-07
      • 2011-03-15
      • 1970-01-01
      相关资源
      最近更新 更多