【问题标题】:Is there a way around this c# type inference failure?有没有办法解决这个 c# 类型推断失败?
【发布时间】:2013-08-05 16:02:04
【问题描述】:

我有以下 IObserver 的 C# 扩展方法。它使我能够通过使用中间主题将 LINQ 表达式放在它的开头。

/// <summary>
/// Given an IObserver we may wish to build a new IObserver that has LINQ
/// combinators at it's head.
///
///     observer = observer.Wrap(observable=>observable.Where(k=>k>0));
///
/// </summary>
public static IObserver<U>
 Wrap<T,U>
 ( this IObserver<T> This
 , Func<IObservable<U>, IObservable<T>> fn 
 )
{
    var s = new Subject<U>();
    fn(s).Subscribe(This);
    return s;
}

但是当我使用该方法时

    [Fact]
    public void WrapShouldWrapObservers()
    {
        var b = new List<int>() { 0, 1, 2,3,4,5 };
        var s = new Subject<int>();
        var c = new List<int>();

        var obs = s.Wrap(observable => observable.Where(k => k > 3));

        s.Subscribe(v => c.Add(v));

        b.ToObservable().Subscribe(obs);
        c.Should().BeEquivalentTo(4, 5);

    }

我得到的错误是

Error   2   The type arguments for method

ReactiveUI.Ext.IObservableMixins.Wrap<T,U>
( System.IObserver<T>
, System.Func<System.IObservable<U>,System.IObservable<T>>
)

cannot be inferred from the usage. Try specifying the type arguments
explicitly. 

当我明确输入类型 args 时,它就可以工作了。

    var obs = s.Wrap<int,int>(observable => observable.Where(k => k > 3));

但是,如果我将类型 args 排除在外,则从检查结果来看是没有歧义的。这里有什么问题?

------------答案------------

如果我更改测试用例以正确执行类型,那么问题出在哪里就很明显了

    [Fact]
    public void WrapShouldWrapObservers()
    {
        var b = new List<int>() { 0, 1, 2,3,4,5 };
        var s = new Subject<string>();
        var c = new List<string>();

        var obs2 = s.Wrap<int,string>(observable => observable.Where(k => k > 3).Select(k=>k.ToString()));

        s.Subscribe(v => c.Add(v));

        b.ToObservable().Subscribe(obs2);
        c.Should().BeEquivalentTo("4", "5");
    }

不可能知道 lambda 的第一个参数 observable 应该是什么。 lambda 必须返回 IObservable,但有无数种 observable 类型可以实现这一点。

【问题讨论】:

    标签: c# system.reactive type-inference


    【解决方案1】:

    问题是您的 lambda 中的观察者可以是任何类型。如果您只是要使用无法转换类型的 LINQ 运算符,您可以这样做:

    public static IObserver<T> Wrap<T> (this IObserver<T> this, 
        Func<IObservable<T>, IObservable<T>> fn)
    {
        var s = new Subject<T>();
        fn(s).Subscribe(this);
        return s;
    }
    

    否则,您将不得不采用不同的方式,指定 IObserver 类型:

    var obs = s.Wrap((IObserver<int> observer) => observer.Where(k => k > 3));
    

    【讨论】:

      【解决方案2】:

      问题是没有办法,使用lambda表达式从参数中推断出类型U

      您可以通过在别处指定表达式类型来绕过 ,如下所示:

      Func<IObservable<int>, IObservable<int>> wrapper = o => o.Where(k => k > 3);
      var obs = s.Wrap(wrapper);
      

      或者这个

      private IObservable<int> Wrapper(IObservable<int>> o)
      {
          return o.Where(k => k > 3);
      }
      
      var obs = s.Wrap(this.Wrapper);
      

      你也可以通过创建一个不同的重载来绕过它,可以从参数中推断出来:

      public static IObserver<T> Wrap<T>
          ( this IObserver<T> This
          , Func<IObservable<T>, IObservable<T>> fn )
      {
          return This.Wrap<T, T>(fn);
      }
      
      var obs = s.Wrap(observer => observer.Where(k => k > 3));
      

      当然在这种情况下,这只有在参数类型和返回类型相同时才有效。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2020-02-25
        • 1970-01-01
        • 1970-01-01
        • 2020-12-13
        • 2020-09-16
        • 2022-09-27
        • 2019-12-10
        • 2016-09-21
        相关资源
        最近更新 更多