Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/301.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/jquery-ui/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# IEnumerable的实现<;T>;为foreach工作,但不是LINQ_C#_Generics_Ienumerable - Fatal编程技术网

C# IEnumerable的实现<;T>;为foreach工作,但不是LINQ

C# IEnumerable的实现<;T>;为foreach工作,但不是LINQ,c#,generics,ienumerable,C#,Generics,Ienumerable,我目前使用以下基本集合 abstract class BaseCollection : Collection<BaseRecord> { } abstract class BaseCollection<TRecord> : Collection<TRecord> where TRecord : BaseRecord,new() { } abstract class BaseCollection<TReco

我目前使用以下基本集合

  abstract class BaseCollection : Collection<BaseRecord>
  {       
  }
  abstract class BaseCollection<TRecord> : Collection<TRecord> where TRecord : BaseRecord,new()
  {

  }
   abstract class BaseCollection<TRecord> : BaseCollection,IEnumerable<TRecord> where TRecord : BaseRecord,new()
   {
      public new IEnumerator<TRecord> GetEnumerator()
      {
         return Items.Cast<TRecord>().GetEnumerator(); 
      }
   }
这是上面使用的两条记录。谢谢

   class DerivedRecord : BaseRecord
   {
      public string DerivedProperty { get; set; }
   }

   abstract class BaseRecord
   {
      public string BaseProperty { get; set; }
   }
这是派生的集合

   class DerivedCollection : BaseCollection<DerivedRecord>
   {

   }
class-DerivedCollection:BaseCollection
{
}

甚至更难看,但可以:

(IEnumerable<DerivedRecord>)derivedCollection).Where(d => d.DerivedProperty == "")
(IEnumerable)derivedCollection)。其中(d=>d.DerivedProperty==“”)
Foreach使用一些“魔法”来确定要循环的类型。它不需要集合支持任何内容的
IEnumerable
。这可能解释了这种差异

问题是编译器无法找出
Where
的泛型类型参数。如果您这样做,它将工作:

IEnumerable<DerivedRecord> ie = derivedCollection;
ie.Where(d => d.DerivedProperty == "");
在这里,我仍然在实例化
集合
,但是作为一个单独的对象而不是基类。然后根据需要将内容转发给它以模拟其API

派生的泛型版本需要完全实现
IEnumerable

R1
R2
类似于两个记录类。在您的版本中,
R2
继承了
R1
——但这与此问题无关,所以我省略了它
I
是通用接口,代表
IEnumerable
。但是我的版本没有办法!它们也与这个问题无关

然后,我将集合类继承系统折叠为两层。基类
C1
实现
I
,然后派生类
C2
选择请求
C2
实现
I
,然后还直接实现
I
。层数也没有区别。使用
where
声明的类型约束也不存在,因此这里也省略了它们

结果是
C2
有两种
I
的实现:
I
I
。一个是从
C1
继承的,另一个是自己添加的

最后我得到了一个方法
M
,它代表Linq中的
Where
。它不需要是一个扩展方法来显示这个问题,所以为了清晰起见,我将它作为一个普通的静态方法

因此,当我们调用方法
M
时,编译器必须弄清楚
T
是什么。它通过查看我们传递给该方法的唯一参数来实现这一点,该方法必须支持
I
。不幸的是,我们正在传递支持
I
I
的内容,那么类型推断过程如何在它们之间做出选择呢?不可能

由于我的界面没有方法,显然,将
new
放在方法前面对我没有帮助,这就是为什么它对你没有帮助。问题不是决定调用接口中的哪个方法,而是将
M
的参数视为
I
还是
I

为什么编译器不将此报告为类型推断问题?根据C#3.0规范,它只是没有先运行类型推断,生成一组可用的重载,然后运行重载解析以选择最佳选择。如果类型推断无法决定泛型方法的两个可能扩展,它会消除这两个扩展,因此重载解析甚至不会看到它们,因此错误消息说没有任何可用的方法称为
M

(但是如果您有Resharper,它有自己的编译器,它使用它在IDE中提供更详细的错误,在本例中,它特别指出:“方法M的类型参数不能从用法中推断出来”。)

现在,为什么
foreach
不同?因为它甚至都不是类型安全的!它可以追溯到添加泛型之前。它甚至不看接口。它只是在循环通过的任何类型中查找名为
GetEnumerator
的公共方法。例如:

public class C
{
    public IEnumerator GetEnumerator() { return null; }
}
就编译器而言,这是一个非常好的集合!(当然,它会在运行时爆炸,因为它返回null
IEnumerator
),但请注意,没有
IEnumerable
或任何泛型。这意味着
foreach
执行隐式强制转换

因此,为了将其与您的代码关联起来,您有一个从
BaseRecord
DerivedRecord
的“向下转换”,您可以使用Linq中的
cast
操作符实现该转换。不管怎样,
foreach
都是为你做的。在我上面的示例中,
C
实际上是
object
类型的项的集合。但我可以写:

foreach (string item in new C())
{
    Console.WriteLine(item.Length);
}
编译器愉快地插入了一个从
对象
字符串
的静默转换。那些东西可能是任何东西。。。恶心


这就是为什么
var
的出现非常好-始终使用
var
声明
foreach
循环变量,这样编译器就不会插入强制转换。它将使变量成为编译时从集合中推断出的最特定类型。

op可能已经解决了这个问题,但是如果它对其他人有帮助,那么当您尝试使用BaseCollection时,您现有的Linq将中断,因为它有两种IEnumerable类型:
IEnumerable
IEnumerable
(继承自BaseCollection->
Collection
)。若要在拥有两个IEnumerable类型的情况下编译现有Linq,请通过实现
IQueryable
定义希望Linq使用的IEnumerable

抽象类BaseCollection:BaseCollection,IEnumerable,IQueryable其中TRecord:BaseRecord,new() { 公共新IEnumerator GetEnumerator() { 返回Items.Cast().GetEnumerator(); } #区域可扩展实现 公共类型ElementType { 获取{return typeof(TRecord);} } public System.Linq.Expressions.Expression表达式 { 获取{返回this.ToList().AsQueryable().Expression;} } 公共IQueryProvider提供程序 { 获取{返回this.ToList().AsQueryable().Provider;} } #恩德
abstract class BaseCollection
{
    private readonly Collection<BaseRecord> _realCollection = new Collection<BaseRecord>();

    public void Add(BaseRecord rec)
    {
        _realCollection.Add(rec);
    }

    public IEnumerable<BaseRecord> Items
    {
        get { return _realCollection; }
    }
}
class BaseCollection<TRecord> : BaseCollection, IEnumerable<TRecord> 
                                    where TRecord :  BaseRecord,new()
{
    public IEnumerator<TRecord> GetEnumerator()
    {
        return Items.Cast<TRecord>().GetEnumerator(); 
    }

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
}
public class R1 { }
public class R2 { }
public interface I<T> { }
public class C1<T> : I<T> { }
public class C2 : C1<R1>, I<R2> { }

class Program
{
    public static I<T> M<T>(I<T> i) { return i; }

    static void Main(string[] args)
    {
        var c2 = new C2();
        var v = M(c2); // Compiler error - no definition for M
    }
}
public class C
{
    public IEnumerator GetEnumerator() { return null; }
}
foreach (string item in new C())
{
    Console.WriteLine(item.Length);
}
abstract class BaseCollection<TRecord> : BaseCollection, IEnumerable<TRecord>, IQueryable<TRecord> where TRecord : BaseRecord, new()
{
    public new IEnumerator<TRecord> GetEnumerator()
    {
        return Items.Cast<TRecord>().GetEnumerator();
    }

    #region IQueryable<TRecord> Implementation
    public Type ElementType
    {
        get { return typeof(TRecord); }
    }

    public System.Linq.Expressions.Expression Expression
    {
        get { return this.ToList<TRecord>().AsQueryable().Expression; }
    }

    public IQueryProvider Provider
    {
        get { return this.ToList<TRecord>().AsQueryable().Provider; }
    }
    #endregion
}