代码之家  ›  专栏  ›  技术社区  ›  Mohammad Nikravesh

.NET对不同参数重载方法的误解(调用不明确)

  •  15
  • Mohammad Nikravesh  · 技术社区  · 6 年前

    我有一些重载方法的问题,我将尝试给出一个简单的实现。

    因此,类包含以下两种方法:

    public class MyRepo<TEntity>
    {
        public List<TEntity> GetData(Expression<Func<TEntity, Boolean>> expression)
        {
            //Do something
        }
    
        public List<TEntity> GetData(Func<TEntity,Boolean> whereClause)
        {
            //Do something
        }
    }
    

    这是我的实体:

    public class MyEntity
    {
        public int Id { get; set; }
        public string Name { get; set; }
    }
    

    这里是我使用它的地方:

    {
        ...
        MyRepo<MyEntity> myRepo = new MyRepo<MyEntity>();
        myRepo.GetData(x => x.Id == 1); // The ambiguity point
        ...
    }
    

    问题是,我只有两个方法具有相同的名称和不同的参数,因此,基于OOP多态性概念,我希望.NET能够理解我想要的方法。

    但很明显.net 不能 理解它是因为 Expression<Func<TEntity, Boolean>> Func<TEntity, Boolean> 相同,这是.NET引发的编译时错误:

    The call is ambiguous between the following methods or properties:
        'Program.MyRepo<TEntity>.GetData(Expression<Func<TEntity, bool>>)' and
        'Program.MyRepo<TEntity>.GetData(Func<TEntity, bool>)'
    

    问题是:如何防止这个编译时错误?

    我的偏好是不要碰我打电话的方式 GetData() 在这一行:

    myRepo.GetData(x => x.Id == 1);
    
    5 回复  |  直到 6 年前
        1
  •  12
  •   Alexei Levenkov    6 年前

    lambda表达式( x=> x.Id==1 )本身没有类型-它们自动“强制转换”到匹配类型的表达式或func/委托 当类型已知时 . 即。 Why must a lambda expression be cast when supplied as a plain Delegate parameter 处理不同委托类型之间的类似问题。

    在您的案例中,潜在的候选方法表明变量和编译器都不能做出选择。

    如果必须保持相同的名称,则调用方必须自己指定类型:

     myRepo.GetData((Expression<Func<TEntity, Boolean>>)(x => x.Id == 1));
     myRepo.GetData((Func<TEntity, Boolean>)(x => x.Id == 2));
    

    我不认为您可以对其中一个选项使用扩展方法,因为搜索将在类级别停止。因此,真正拥有具有不同名称的方法是唯一真正的选择(如果您需要这两种方法的话)。如果只是考虑 Expression 版本就足够了。或者,您可以在不同的类之间拆分它们(类似于 IQueryable 表情 当类似的方法 IEnumerable 采取FUNC(见) QueryableExtenasions )

        2
  •  8
  •   Siavash Rostami Raju K    6 年前

    我相信消除过载混乱的最简单方法是在将输入发送到函数之前对其进行强制转换。这可以隐式(内联)完成,也可以以定义类型化输入(推荐方式)的形式完成,而不是以匿名方式完成。以下是我如何测试它,它在不发出警告的情况下工作。

    MyRepo<MyEntity> myRepo = new MyRepo<MyEntity>();
    Func<MyEntity, bool> predicate = x => x.Id == 1;
    Expression<Func<MyEntity, bool>> expression = x => x.Id == 1;
    // both below lines are fine now
    myRepo.GetData(predicate);
    myRepo.GetData(expression);
    

    显然,C编译器不够精确,无法区分两者,因为它需要一些启发式行为,匿名输入本质上是相同的。无论如何,这种解决方法可以解决这个问题。

        3
  •  4
  •   Fredy Adriano Jimenez Martinez    6 年前

    问题是,当您编译表达式时,您将使用与第二个相同的签名创建一个方法。

    我建议您更改第一个方法的名称

    另外,如果要使用表达式,则返回一个IQueryable以利用延迟的执行。

        4
  •  4
  •   CodeMan    6 年前

    我换了你的课,解决了问题:

    public class MyRepo<TEntity>
    {
        public void GetData(Expression<Func<TEntity, bool>> expression, out List<TEntity> result)
        {
            result = null;
        }
    
        public List<TEntity> GetData(Func<TEntity, bool> whereClause)
        {
            return null;
        }
    }
    
    private void button1_Click(object sender, EventArgs e)
    {
        var myRepo = new MyRepo<MyEntity>();
        var i = myRepo.GetData(x => x.Id == 1);
        myRepo.GetData(x => x.Id == 1, out i);
    }
    
        5
  •  4
  •   Vitor Paulino    6 年前

    考虑对这两个方法使用接口继承。根据坚实的原则,您应该避免使用具体类型的引用,而应该通过接口使用抽象。像这样:

    public interface IQueryDataByPredicateExpression 
    {
    List<TEntity> GetData(Expression<Func<TEntity, Boolean>> whereClause);
    }
    
    public interface IQueryDataByPredicate
    {
    List<TEntity> GetData(Func<TEntity,Boolean> whereClause);
    
    }
    
        public class MyRepo<TEntity> : IQueryDataByPredicateExpression, IQueryDataByPredicate
        {
            public List<TEntity> GetData(Expression<Func<TEntity, Boolean>> expression)
            {
                //Do something
            }
    
            public List<TEntity> GetData(Func<TEntity,Boolean> whereClause)
            {
                //Do something
            }
        }
    

    现在,根据您的查询方式,您应该使用所需接口类型的变量。现在,每个接口都应该有一个引用,引用同一个repo实例。

    IQueryDataByPredicateExpression  queryRepoWithPredicateExpression = myRepo as IQueryDataByPredicateExpression;
    
    IQueryDataByPredicate queryRepoWithPredicate = myRepo as IQueryDataByPredicate;
    
    
     queryRepoWithPredicateExpression.GetData(x => x.Id == 1);
     queryRepoWithPredicate.GetData(x => x.Id == 2);
    

    但是如果你不能,或者想改变你调用这些方法的方式,那么亚历克赛·列文科夫的回答看起来很好。