Я пишу большой статический метод, который принимает универсальный параметр в качестве аргумента. Я вызываю этот метод, и фреймворк генерирует исключение System.InvalidProgramException. Это исключение вызывается еще до выполнения первой строки метода.
Я могу создать статический класс, который принимает общий аргумент, а затем сделать его методом статического класса, и все будет работать нормально.
Это дефект .NET или есть какое-то неясное общее правило, которое я здесь нарушаю?
Для полноты картины я включил метод, который не работает, и метод, который проходит. Обратите внимание, что здесь используется ряд других классов из моей собственной библиотеки (например, GridUtils), и эти классы здесь не объясняются. Я не думаю, что реальный смысл имеет значение: вопрос в том, почему происходит сбой среды выполнения еще до запуска метода.
(Я программирую с помощью Visual Studio 2005, так что, возможно, это исчезло в Visual Studio 2008.)
Это вызывает исключение перед вызовом первой строки:
private delegate void PROG_Delegate<TGridLine>(DataGridView dgv, IEnumerable<TGridLine> gridLines, string[] columns);
public static void PopulateReadOnlyGrid<TGridLine>(DataGridView dgv, IEnumerable<TGridLine> gridLines, string[] columns)
{
if (dgv.InvokeRequired)
{
dgv.BeginInvoke
(
new PROG_Delegate<TGridLine>(PopulateReadOnlyGrid<TGridLine>),
new object[] { dgv, gridLines, columns }
);
return;
}
GridUtils.StatePreserver statePreserver = new GridUtils.StatePreserver(dgv);
System.Data.DataTable dt = CollectionHelper.ConvertToDataTable<TGridLine>((gridLines));
dgv.DataSource = dt;
dgv.DataMember = "";
dgv.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.AllCells;
GridUtils.OrderColumns<TGridLine>(dgv, columns);
statePreserver.RestoreState();
dgv.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.None;
}
Это отлично работает:
public static class Populator<TGridLine>
{
private delegate void PROG_Delegate(DataGridView dgv, IEnumerable<TGridLine> gridLines, string[] columns);
public static void PopulateReadOnlyGrid(DataGridView dgv, IEnumerable<TGridLine> gridLines, string[] columns)
{
if (dgv.InvokeRequired)
{
dgv.BeginInvoke
(
new PROG_Delegate(PopulateReadOnlyGrid),
new object[] { dgv, gridLines, columns }
);
return;
}
GridUtils.StatePreserver statePreserver = new GridUtils.StatePreserver(dgv);
System.Data.DataTable dt = CollectionHelper.ConvertToDataTable<TGridLine>((gridLines));
dgv.DataSource = dt;
dgv.DataMember = "";
dgv.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.AllCells;
GridUtils.OrderColumns<TGridLine>(dgv, columns);
statePreserver.RestoreState();
dgv.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.None;
}
}
Пространство имен GridUtils является частью моей собственной библиотеки. Я поясню, что это сообщение, спасибо.
В прямом ответе на ваш вопрос, он находится в WinForms.





Просто к вашему сведению, не уверен, что это что-то исправит, но ваш метод Invoke можно упростить. Это также устраняет необходимость в этом делегате (что может привести к исправлению?):
dgv.BeginInvoke(new MethodInvoker(delegate()
{
PopulateReadOnlyGrid(dgv, gridLines, columns);
}));
Ваш код отлично работает для меня, когда я вставляю его в форму (после того, как закомментировал ваш материал GridUtils). Я даже вызываю метод как из потока с графическим интерфейсом, так и из потока без графического интерфейса. Пробовал в 3.5 и 2.0. Работает отлично.... (!?)
Попробуйте этот код:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Reflection;
using System.Threading;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private delegate void PROG_Delegate<TGridLine>(Control dgv, IEnumerable<TGridLine> gridLines, string[] columns);
public static void PopulateReadOnlyGrid<TGridLine>(Control dgv, IEnumerable<TGridLine> gridLines, string[] columns)
{
if (dgv.InvokeRequired)
{
dgv.BeginInvoke
(
new PROG_Delegate<TGridLine>(PopulateReadOnlyGrid<TGridLine>),
new object[] { dgv, gridLines, columns }
);
return;
}
MessageBox.Show("hi");
//GridUtils.StatePreserver statePreserver = new GridUtils.StatePreserver(dgv);
//System.Data.DataTable dt = CollectionHelper.ConvertToDataTable<TGridLine>((gridLines));
//dgv.DataSource = dt;
//dgv.DataMember = "";
//dgv.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.AllCells;
//GridUtils.OrderColumns<TGridLine>(dgv, columns);
//statePreserver.RestoreState();
//dgv.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.None;
}
private void button1_Click(object sender, EventArgs e)
{
PopulateReadOnlyGrid(this, new int[] { 1, 2, 3 }, new string[] { "a" });
ThreadPool.QueueUserWorkItem(new WaitCallback((a) =>
{
PopulateReadOnlyGrid(this, new int[] { 1, 2, 3 }, new string[] { "a" });
}));
}
}
}
Обновлено из-за того, что я неверно истолковал пример кода.
Попробуйте обернуть делегата MethodInvoker:
http://msdn.microsoft.com/en-us/library/system.windows.forms.methodinvoker.aspx
TGridLine не указан родительским классом. Фактически, он полностью исключил определение родительского класса в своем первом примере. Это правильное обращение с ними.
Черт, есть класс, о котором я бы хотел знать. В моем коде полно специализированных делегатов, от которых я буду счастлив избавиться.
Следуя предложению Программное обеспечение джедаев, я провел несколько тестов и убедительно доказал, что именно использование PROG_Delegate вызывает исключение.
Если я использую MethodInvoker, код работает без ошибок.
private delegate void PROG_Delegate<TGridLine>(DataGridView dgv, IEnumerable<TGridLine> gridLines, string[] columns);
public static void PopulateReadOnlyGrid<TGridLine>(DataGridView dgv, IEnumerable<TGridLine> gridLines, string[] columns)
{
if (dgv.InvokeRequired)
{
dgv.BeginInvoke(new MethodInvoker(delegate()
{
PopulateReadOnlyGrid(dgv, gridLines, columns);
}));
return;
}
GridUtils.StatePreserver statePreserver = new GridUtils.StatePreserver(dgv);
System.Data.DataTable dt = CollectionHelper.ConvertToDataTable<TGridLine>((gridLines));
dgv.DataSource = dt;
dgv.DataMember = "";
dgv.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.AllCells;
GridUtils.OrderColumns<TGridLine>(dgv, columns);
statePreserver.RestoreState();
dgv.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.None;
}
Если я верну PROG_Delegate и удалю все остальное, я верну исключение.
private delegate void PROG_Delegate<TGridLine>(DataGridView dgv, IEnumerable<TGridLine> gridLines, string[] columns);
public static void PopulateReadOnlyGrid<TGridLine>(DataGridView dgv, IEnumerable<TGridLine> gridLines, string[] columns)
{
if (dgv.InvokeRequired)
{
dgv.BeginInvoke
(
new PROG_Delegate<TGridLine>(PopulateReadOnlyGrid<TGridLine>),
new object[] { dgv, gridLines, columns }
);
}
//GridUtils.StatePreserver statePreserver = new GridUtils.StatePreserver(dgv);
//System.Data.DataTable dt = CollectionHelper.ConvertToDataTable<TGridLine>((gridLines));
//dgv.DataSource = dt;
//dgv.DataMember = "";
//dgv.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.AllCells;
//GridUtils.OrderColumns<TGridLine>(dgv, columns);
//statePreserver.RestoreState();
//dgv.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.None;
}
Итак, есть простой обходной путь, а использование MethodInvoker делает код более лаконичным и читаемым.
На теоретическом уровне все еще остается вопрос, почему он дает сбой, учитывая, что PROG_Delegate является законным и работает для других людей. Я подозреваю, что лучший ответ, к которому мы можем прийти, - это «какая-то непонятная ошибка», которая останется неясной, потому что кодеры все равно предпочитают использовать Method Invoker.
Это для ASP.NET, WinForms или чего? Что такое пространство имен GridUtils?