【问题标题】:Difference between System.Collections.Generic.List<T>.ToArray() and System.Linq.Enumerable.ToArray<T>()?System.Collections.Generic.List<T>.ToArray() 和 System.Linq.Enumerable.ToArray<T>() 之间的区别?
【发布时间】:2012-05-08 07:37:09
【问题描述】:

我最近在使用某种网络方法时遇到了相当大的问题:

void CheckGfiHelpdesks(string ticket, GfiCheck[] newHelpdeskChecks, GfiCheck[] otherChecks)

我一直在用这段代码调用那个方法:

List&lt;GfiCheck&gt; newFailedChecks = new List&lt;GfiCheck&gt;();

List&lt;GfiCheck&gt; otherFailedChecks = new List&lt;GfiCheck&gt;();

//do some work, create new GfiCheck items, fill the lists

Webclient.CheckGfiHelpdesks(Ticket, newFailedChecks.ToArray(), otherFailedChecks.ToArray());

newFailedChecks 和 otherFailedChecks 是列表。当该方法作为 SOAP 服务在 IIS 上运行时,这一直运行良好。

但是,在我将完全相同的方法复制到 WCF 服务后,调用产生了“400 bad request”异常。

最终我发现 .ToArray() 确实是问题所在。这个:

Webclient.CheckGfiHelpdesks(Ticket, newFailedChecks.ToArray&lt;GfiCheck&gt;(), otherFailedChecks.ToArray&lt;GfiCheck&gt;());

即使用System.Linq.Enumerable.ToArray&lt;T&gt;() 而不是System.Collections.Generic.List&lt;T&gt;.ToArray() 最终解决了问题并且异常消失了。

对这种差异的解释是什么?数组是数组,但显然不是?

确切的例外是:

System.ServiceModel.ProtocolException

远程服务器返回了意外响应:(400) Bad Request。

堆栈跟踪:

服务器堆栈跟踪:

在 System.ServiceModel.Channels.HttpChannelUtilities.ValidateRequestReplyResponse(HttpWebRequest 请求,HttpWebResponse 响应,HttpChannelFactory 工厂,WebException responseException,ChannelBinding 通道绑定)

在 System.ServiceModel.Channels.HttpChannelFactory.HttpRequestChannel.HttpChannelRequest.WaitForReply(TimeSpan 超时)

在 System.ServiceModel.Channels.RequestChannel.Request(消息消息,TimeSpan 超时)

在 System.ServiceModel.Dispatcher.RequestChannelBinder.Request(消息消息,TimeSpan 超时)

在 System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean oneway, ProxyOperationRuntime operation, Object[] ins, Object[] outs, TimeSpan timeout)

在 System.ServiceModel.Channels.ServiceChannelProxy.InvokeService(IMethodCallMessage methodCall, ProxyOperationRuntime 操作)

在 System.ServiceModel.Channels.ServiceChannelProxy.Invoke(IMessage 消息)

>

在 [0] 处重新抛出异常:

在 System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg, IMessage retMsg)

在 System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 类型)

在 MonitoringService.BL.CentronService.ICentronService.CheckGfiHelpdesks(String ticket, GfiCheck[] newHelpdeskChecks, GfiCheck[] otherChecks)

在 C:\Users\sohrm\documents\visual studio 2010\Projects\MonitoringService\MonitoringService.BL\Service 中的 MonitoringService.BL.CentronService.CentronServiceClient.CheckGfiHelpdesks(String ticket, GfiCheck[] newHelpdeskChecks, GfiCheck[] otherChecks) References\CentronService\Reference.cs:Zeile 5368。

在 C:\Users\sohrm\documents\visual studio 2010\Projects\MonitoringService\MonitoringService.BL\ConnectorBL.cs:Zeile 120 中的 MonitoringService.BL.ConnectorBL.CheckHelpdesks(List`1 clients)。

在 C:\Users\sohrm\documents\visual studio 2010\Projects\MonitoringService\MonitoringService.Client\MainForm.cs:Zeile 124 中的 MonitoringService.WinForm.MainForm.LoadChecks()。

在 C:\Users\sohrm\documents\visual studio 2010\Projects\MonitoringService\MonitoringService.Client\MainForm.cs:Zeile 114 中的 MonitoringService.WinForm.MainForm.btnLoad_Click(Object sender, EventArgs e)。

在 System.Windows.Forms.Control.OnClick(EventArgs e)

在 DevExpress.XtraEditors.BaseButton.OnClick(EventArgs e)

在 DevExpress.XtraEditors.BaseButton.OnMouseUp(MouseEventArgs e)

在 System.Windows.Forms.Control.WmMouseUp(Message& m, MouseButtons button, Int32 clicks)

在 System.Windows.Forms.Control.WndProc(Message&m)

在 DevExpress.Utils.Controls.ControlBase.WndProc(Message& m)

在 DevExpress.XtraEditors.BaseControl.WndProc(Message& msg)

在 System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m)

在 System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message&m)

在 System.Windows.Forms.NativeWindow.DebuggableCallback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)

在 System.Windows.Forms.UnsafeNativeMethods.DispatchMessageW(MSG& msg)

在 System.Windows.Forms.Application.ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(IntPtr dwComponentID, Int32 reason, Int32 pvLoopData)

在 System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(Int32 原因,ApplicationContext 上下文)

在 System.Windows.Forms.Application.ThreadContext.RunMessageLoop(Int32 原因,ApplicationContext 上下文)

在 System.Windows.Forms.Application.Run(Form mainForm)

在 C:\Users\sohrm\documents\visual studio 2010\Projects\MonitoringService\MonitoringService.Client\Program.cs:Zeile 22 中的 MonitoringService.WinForm.Program.Main()。

在 System.AppDomain._nExecuteAssembly(RuntimeAssembly 程序集,String[] args)

在 System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)

在 Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()

在 System.Threading.ThreadHelper.ThreadStart_Context(对象状态)

at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean ignoreSyncCtx)

在 System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)

在 System.Threading.ThreadHelper.ThreadStart()

【问题讨论】:

标签: c# wcf linq web-services soap


【解决方案1】:

System.Collections.Generic.List&lt;T&gt;.ToArray()System.Linq.Enumerable.ToArray&lt;T&gt;() 之间应该没有区别。让我们看看里面发生了什么:

System.Collections.Generic.List&lt;T&gt; 只是创建新数组并将内部项目数组复制到它:

public T[] ToArray()
{
    T[] destinationArray = new T[this._size];
    Array.Copy(this._items, 0, destinationArray, 0, this._size);
    return destinationArray;
}

System.Linq.Enumerable无法访问list的内部items数组,所以通过buffer创建数组:

public static TSource[] ToArray<TSource>(this IEnumerable<TSource> source)
{
    if (source == null)    
        throw Error.ArgumentNull("source");

    Buffer<TSource> buffer = new Buffer<TSource>(source);
    return buffer.ToArray();
}

缓冲区内发生了什么? List&lt;T&gt; 是一个 ICollection&lt;T&gt;,因此它只调用 CopyTo 实现 List&lt;T&gt;

internal Buffer(IEnumerable<TElement> source)
{
   TElement[] array = null;
   ICollection<TElement> is2 = source as ICollection<TElement>;
   length = is2.Count;
   if (length > 0)
   {
       array = new TElement[length];
       // implemented as Array.Copy(this._items, 0, array, 0, this._size);
       is2.CopyTo(array, 0);
   }

   this.items = array;
   this.count = length;
}

如您所见,通过列表的方法 CopyTo 将项目复制到新数组中,该方法与 ToArray 方法中的作用完全相同。但是使用System.Linq.Enumerable 你有一个小缺点 - 将列表项复制到缓冲区后,会创建另一个数组,并将缓冲区中的项复制到该数组:

internal TElement[] ToArray()
{
   if (this.count == 0)        
       return new TElement[0];

   if (this.items.Length == this.count)        
       return this.items;

   TElement[] destinationArray = new TElement[this.count];
   Array.Copy(this.items, 0, destinationArray, 0, this.count);
   return destinationArray;
}

因此,在这两种情况下,列表中的项目都通过相同的方法 Array.Copy 复制到新数组中。但在Enumerable 的情况下,这种情况会发生两次。如果我处理List,我宁愿使用列表的ToArray 实现。

【讨论】:

  • 顺便检查一下您的服务配置。可能的错误原因是最大消息大小的设置:stackoverflow.com/questions/784606/…
  • 感谢您在此答案中所做的所有工作,但事实仍然存在:当我将 ToArray() 传递给 WCF 服务时,它会给我一个异常,而 ToArray 却没有。因此,当明显存在差异时,说“不应该有差异”并不是很有帮助。可能问题出在 WCF 上,但是哪里有问题。
  • 我已经检查过了。任何最大消息大小都会发生异常,并且我已经测试了每个数组的 1 个项目。每个 GfiCheck 项目都是少量的整数和小字符串,如果那样的话,只有几 kb。
  • newFailedChecks 列表的确切类型是什么?
  • 两个列表都是List,填充了GfiCheck项。不涉及继承类型,每个项目都是相同的类型。相应地编辑了问题。
猜你喜欢
  • 2015-04-08
  • 2015-11-15
  • 1970-01-01
  • 2011-05-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-09-07
相关资源
最近更新 更多