C#在推断出的操作中抛出异常,可以解决重载问题

问题描述 投票:3回答:1

我碰到类型系统的一个非常烦人的极端情况。

我已将代码减少到最低要求以显示问题所在。

using System;

// Some interface or Base class, doesn't matter
public interface IFace {}
// Some class that implements/extends it
public class Implements: IFace {}

public static class Foo {

  public static void Bar<T, T1>(Func<T> f) where T1: IFace {
    Console.WriteLine("Bar relaxed");
    var _ = f();
  }

  public static void Bar<T1, T2>(Action f)
    where T1: IFace
    where T2: IFace
  {
    Console.WriteLine("Bar strict");
    f();
  }

  public static void Main() {
    try {
      Bar<Implements, Implements>(() => { // Should call Bar strict
        var _ = new Implements();
      });

      Bar<Implements, Implements>(() => { // Should still call Bar strict
        var _ = new Implements();
        throw new NullReferenceException(); // But having unconditional throw in the method
                                            // Makes it a `Func<T>` instead of a `Action`
      });
    } catch(Exception _) {}
  }
}

我想要的输出是

Bar strict
Bar strict

我得到的输出是

Bar strict
Bar relaxed

回复:https://repl.it/repls/WearyImpoliteExponent

可以解决吗? (不删除第一个Bar,或更改通用参数的数量)

在实际代码中,Bar方法都不返回void,它们返回引用通用参数的内容,并且它们的主体也不同

编辑:为澄清起见,“真实世界” Bar方法看起来更像这样:

public static Baz<T, IFace> Bar<T, T1>(Func<T> f) where T1: IFace;

public static Baz<Default, IFace> Bar<T1, T2>(Action f)
  where T1: IFace
  where T2: IFace;

// Where `Default` is a concrete type
struct Default {}
c# generics exception overloading type-inference
1个回答
1
投票

是的,您可以这样做。技巧是在引发的异常之后添加return;

Foo.Bar<Implements, Implements>(() =>
{ // Should still call Bar strict
    var _ = new Implements();
    throw new NullReferenceException();
    return;
}

lambda现在将正确解析为Action而不是Func

© www.soinside.com 2019 - 2024. All rights reserved.