防止将重复对象添加到 List<object>,同时保留具有两个同名对象的能力?
Prevent duplicate objects being added to List<object> while retaining the ability to have two object of the same name?
我已经尝试了一遍又一遍,但我现在只是在兜圈子。
我在外部存储了一组大部分独特的作业文件,我从这些文件中收集信息并通过列表将其存储在我的程序中。
有时,我有两个同名的作业文件(不幸的是,这是不可避免的)但包含不同的数据(其中最独特的是 DateTime 字符串)。
我的程序通过轮询这些文件的 3 个位置(所有位置都是镜像的,因此每个位置都存在相同的数据)来工作,收集信息,然后根据当前状态将信息输出到 ListView window每个作业文件。
我遇到的问题是防止将重复的条目添加到我的列表中,导致列表中只有 23 个作业文件有 69 个对象。
为了避免在列表视图中出现重复输出,我尝试使用字典来存储基于作业文件名的已知键,但是当到达与现有条目同名的第二个文件时,这会失败。结果我在列表中只看到 21 个工作,本应包含 23 个工作(我也抛出了异常)。我通过在第二个条目的末尾附加一些内容来解决这个问题,但这并不理想,后来发现会导致其他并发症。
有什么想法吗?
这是我目前所拥有的。
public List<object> PendingJobsArray = new List<object>();
public List<object> ActiveJobsArray = new List<object>();
public List<object> CompletedJobsArray = new List<object>();
public JobObject PendingJobs = new JobObject();
public JobObject ActiveJobs = new JobObject();
public JobObject CompletedJobs = new JobObject();
private void updatePending(int index, XmlParser xmlParser)
{
if (Program.PendingJobsArray.Count == 0)
{
Program.KnownPendingJobs.Add(xmlParser.getElementAttrValue(index, "Name"), xmlParser.getElementAttrValue(index, "DateTime"));
JobObject PendingJobs = new JobObject();
PendingJobs.jobObjectName = xmlParser.getElementAttrValue(index, "Name");
PendingJobs.jobObjectState = xmlParser.getElementAttrValue(index, "State");
PendingJobs.jobObjectDiscName = xmlParser.getElementAttrValue(index, "DiscName");
PendingJobs.jobObjectPercentComplete = Convert.ToInt32(xmlParser.getElementAttrValue(index, "TotalPercentComplete"));
PendingJobs.jobObjectDateTime = xmlParser.getElementAttrValue(index, "DateTime");
PendingJobs.jobObjectRequested = Convert.ToInt32(xmlParser.getElementAttrValue(index, "Requested"));
PendingJobs.jobObjectCompleted = Convert.ToInt32(xmlParser.getElementAttrValue(index, "Completed"));
PendingJobs.jobObjectFailed = Convert.ToInt32(xmlParser.getElementAttrValue(index, "Failed"));
PendingJobs.jobObjectPriority = Convert.ToInt32(xmlParser.getElementAttrValue(index, "Priority"));
// PendingJobs.jobObjectServer = xmlParser.getElementAttrValue("Server");
// PendingJobs.jobObjectOperation = xmlParser.getElementAttrValue("Operation");
Program.PendingJobsArray.Add(PendingJobs);
}
else if (Program.KnownPendingJobs.ContainsKey(xmlParser.getElementAttrValue(index, "Name")))
{
for (int i = 0; i < Program.PendingJobsArray.Count; ++i)
{
if (xmlParser.getElementAttrValue(index, "State") == "Write")
{
Program.PendingJobsArray.RemoveAt(index);
}
else
{
PendingJobs.jobObjectName = xmlParser.getElementAttrValue(index, "Name");
PendingJobs.jobObjectState = xmlParser.getElementAttrValue(index, "State");
PendingJobs.jobObjectDiscName = xmlParser.getElementAttrValue(index, "DiscName");
PendingJobs.jobObjectPercentComplete = Convert.ToInt32(xmlParser.getElementAttrValue(index, "TotalPercentComplete"));
PendingJobs.jobObjectDateTime = xmlParser.getElementAttrValue(index, "DateTime");
PendingJobs.jobObjectRequested = Convert.ToInt32(xmlParser.getElementAttrValue(index, "Requested"));
PendingJobs.jobObjectCompleted = Convert.ToInt32(xmlParser.getElementAttrValue(index, "Completed"));
PendingJobs.jobObjectFailed = Convert.ToInt32(xmlParser.getElementAttrValue(index, "Failed"));
PendingJobs.jobObjectPriority = Convert.ToInt32(xmlParser.getElementAttrValue(index, "Priority"));
Program.PendingJobsArray[index] = PendingJobs;
}
}
}
else if (Program.KnownPendingJobs.ContainsKey(xmlParser.getElementAttrValue(index, "Name")))
}
else
{
Program.KnownPendingJobs.Add(xmlParser.getElementAttrValue(index, "Name"), xmlParser.getElementAttrValue(index, "DateTime"));
JobObject PendingJobs = new JobObject();
PendingJobs.jobObjectName = xmlParser.getElementAttrValue(index, "Name");
PendingJobs.jobObjectState = xmlParser.getElementAttrValue(index, "State");
PendingJobs.jobObjectDiscName = xmlParser.getElementAttrValue(index, "DiscName");
PendingJobs.jobObjectPercentComplete = Convert.ToInt32(xmlParser.getElementAttrValue(index, "TotalPercentComplete"));
PendingJobs.jobObjectDateTime = xmlParser.getElementAttrValue(index, "DateTime");
PendingJobs.jobObjectRequested = Convert.ToInt32(xmlParser.getElementAttrValue(index, "Requested"));
PendingJobs.jobObjectCompleted = Convert.ToInt32(xmlParser.getElementAttrValue(index, "Completed"));
PendingJobs.jobObjectFailed = Convert.ToInt32(xmlParser.getElementAttrValue(index, "Failed"));
PendingJobs.jobObjectQuantity = xmlParser.getElementAttrValue(index, "Quantity");
PendingJobs.jobObjectPriority = Convert.ToInt32(xmlParser.getElementAttrValue(index, "Priority"));
//PendingJobs.jobObjectServer = xmlParser.getElementAttrValue("Server");
//PendingJobs.jobObjectOperation = xmlParser.getElementAttrValue("Operation");
Program.PendingJobsArray.Add(PendingJobs);
}
}
更新 代码基于下面的评论,
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Collections;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
using System.Windows.Forms;
namespace SOERequestProgress
{
class XmlSplitter
{
// Declarations
private static string[] COMPLETED_STATES = new string[2]
{
"ERROR",
"COMPLETE"
};
private static string[] PENDING_STATES = new string[1]
{
"PENDING"
};
private static string[] ACTIVE_STATES = new string[1]
{
"PAUSED"
};
private const string STATE_ERROR = "ERROR";
private const string STATE_COMPLETE = "COMPLETE";
private const string STATE_PENDING = "PENDING";
private const string STATE_ACTIVE = "PAUSED";
public static JobObject PendingJobs = new JobObject();
public static JobObject CompletedJobs = new JobObject();
public static JobObject ActiveJobs = new JobObject();
//public static JobObject x;
public static int intI = 0;
// Add additional info from parsed XML (Currently only "Name" and "State" are collected.
public static void splitXml(string JobList, out List<JobObject> Active, out List<JobObject> Completed, out List<JobObject> Pending)
{
bool flag = false;
int num1 = 0;
string text = "";
string xmlString = "";
while (!flag)
{
if (num1 < 3)
{
try
{
xmlString = JobList;
flag = true;
}
catch (Exception ex)
{
++num1;
text = text + "Try (" + num1.ToString() + ") " + ex.Message + "\n";
Thread.Sleep(1000);
}
}
else
break;
}
if (!flag)
{
int num2 = (int)MessageBox.Show(text, "Job list error", MessageBoxButtons.OK, MessageBoxIcon.Hand);
}
else
{
XmlParser xmlParser = new XmlParser();
if (!xmlParser.setXmlString(xmlString))
{
int num3 = (int)MessageBox.Show("Error parsing job list server response.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Hand);
flag = false;
}
else
{
int num3 = xmlParser.setElementName("Job");
for (int index = 0; index < num3; ++index)
{
string elementAttrValue1 = xmlParser.getElementAttrValue(index, "Name");
string elementAttrValue2 = xmlParser.getElementAttrValue(index, "State");
if (isState(elementAttrValue2, PENDING_STATES))
{
updatePending(index, xmlParser);
}
else if (isState(elementAttrValue2, COMPLETED_STATES))
{
updateCompleted(index, xmlParser);
}
else //if (isState(elementAttrValue2, ACTIVE_STATES))
{
updateActive(index, xmlParser);
}
}
}
}
Active = Program.ActiveJobsArray;
Completed = Program.CompletedJobsArray;
Pending = Program.PendingJobsArray;
}
private static void updatePending(int index, XmlParser xmlParser)
{
var jobName = xmlParser.getElementAttrValue(index, "Name");
var pendingJob = Program.PendingJobsArray.Find(x => x.jobObjectName == jobName);
if (pendingJob == null)
{
JobObject newPendingJob = CreateJob("Pending", index, xmlParser);
Program.PendingJobsArray.Add(newPendingJob);
}
}
private static void updateCompleted(int index, XmlParser xmlParser)
{
var jobName = xmlParser.getElementAttrValue(index, "Name");
var CompletedJob = Program.CompletedJobsArray.Find(x => x.jobObjectName == jobName);
if (CompletedJob == null)
{
JobObject newCompletedJob = CreateJob("Completed", index, xmlParser);
Program.CompletedJobsArray.Add(newCompletedJob);
}
}
private static void updateActive(int index, XmlParser xmlParser)
{
var jobName = xmlParser.getElementAttrValue(index, "Name");
var activeJob = Program.ActiveJobsArray.Find(x => x.jobObjectName == jobName);
if (activeJob == null)
{
JobObject newActiveJob = CreateJob("Active", index, xmlParser);
Program.ActiveJobsArray.Add(newActiveJob);
}
}
private static JobObject CreateJob(string jobType, int index, XmlParser xmlParser)
{
if (jobType == "Pending")
{
JobObject x = new JobObject();
x.jobObjectName = xmlParser.getElementAttrValue(index, "Name");
x.jobObjectState = xmlParser.getElementAttrValue(index, "State");
x.jobObjectDiscName = xmlParser.getElementAttrValue(index, "DiscName");
x.jobObjectPercentComplete = Convert.ToInt32(xmlParser.getElementAttrValue(index, "TotalPercentComplete"));
x.jobObjectDateTime = xmlParser.getElementAttrValue(index, "DateTime");
x.jobObjectRequested = Convert.ToInt32(xmlParser.getElementAttrValue(index, "Requested"));
x.jobObjectCompleted = Convert.ToInt32(xmlParser.getElementAttrValue(index, "Completed"));
x.jobObjectFailed = Convert.ToInt32(xmlParser.getElementAttrValue(index, "Failed"));
x.jobObjectQuantity = xmlParser.getElementAttrValue(index, "Quantity");
x.jobObjectPriority = Convert.ToInt32(xmlParser.getElementAttrValue(index, "Priority"));
//x.jobObjectServer = xmlParser.getElementAttrValue("Server");
//x.jobObjectOperation = xmlParser.getElementAttrValue("Operation");
return x;
}
if (jobType == "Completed")
{
JobObject x = new JobObject();
x.jobObjectName = xmlParser.getElementAttrValue(index, "Name");
x.jobObjectState = xmlParser.getElementAttrValue(index, "State");
x.jobObjectDiscName = xmlParser.getElementAttrValue(index, "DiscName");
x.jobObjectPercentComplete = Convert.ToInt32(xmlParser.getElementAttrValue(index, "TotalPercentComplete"));
x.jobObjectDateTime = xmlParser.getElementAttrValue(index, "DateTime");
x.jobObjectRequested = Convert.ToInt32(xmlParser.getElementAttrValue(index, "Requested"));
x.jobObjectCompleted = Convert.ToInt32(xmlParser.getElementAttrValue(index, "Completed"));
x.jobObjectFailed = Convert.ToInt32(xmlParser.getElementAttrValue(index, "Failed"));
//x.jobObjectServer = xmlParser.getElementAttrValue("Server");
//x.jobObjectOperation = xmlParser.getElementAttrValue("Operation");
return x;
}
if (jobType == "Active")
{
JobObject x = new JobObject();
x.jobObjectName = xmlParser.getElementAttrValue(index, "Name");
x.jobObjectState = xmlParser.getElementAttrValue(index, "State");
x.jobObjectDiscName = xmlParser.getElementAttrValue(index, "DiscName");
x.jobObjectPercentComplete = Convert.ToInt32(xmlParser.getElementAttrValue(index, "TotalPercentComplete"));
x.jobObjectDateTime = xmlParser.getElementAttrValue(index, "DateTime");
x.jobObjectRequested = Convert.ToInt32(xmlParser.getElementAttrValue(index, "Requested"));
x.jobObjectCompleted = Convert.ToInt32(xmlParser.getElementAttrValue(index, "Completed"));
x.jobObjectFailed = Convert.ToInt32(xmlParser.getElementAttrValue(index, "Failed"));
x.jobObjectPriority = Convert.ToInt32(xmlParser.getElementAttrValue(index, "Priority"));
//x.jobObjectServer = xmlParser.getElementAttrValue("Server");
//x.jobObjectOperation = xmlParser.getElementAttrValue("Operation");
return x;
}
return null;
}
private static bool isState(string jobState, string[] knownStates)
{
bool flag = false;
for (int index = 0; index < knownStates.Length; ++index)
{
if (jobState.ToUpper().Equals(knownStates[index]))
{
flag = true;
//break;
}
}
return flag;
}
}
}
这个新代码似乎不正确,我看不出它在哪里失败,但它在几秒钟内用 200 多个条目填充了我的 CompletedJobArray。
我的 ListView 仅显示 1 个条目(针对唯一作业进行过滤)并且在检查数组的内容时,它为所有 200 多个条目添加了相同的 jobObject。
谁能找出这段代码中的缺陷?
更新 2: IEqualityComparer 已实施,遇到更多错误。
下面的代码是Equals和GetHashCode的实现。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace SOERequestProgress
{
class JobComparer : IEqualityComparer<JobObject>
{
public JobComparer(Func<JobObject, object> keySelector)
{
KeySelector = keySelector;
}
public bool Equals(JobObject x, JobObject y)
{
return KeySelector(x).Equals(KeySelector(y));
//x.jobObjectName == y.jobObjectName &&
//x.jobObjectDateTime == y.jobObjectDateTime;
}
public int GetHashCode(JobObject job)
{
return KeySelector(job).GetHashCode();
}
}
}
此实现抛出以下错误,
Error 1 'SOERequestProgress.JobObject' does not implement interface member 'System.Collections.Generic.IEqualityComparer<SOERequestProgress.JobComparer>.GetHashCode(SOERequestProgress.JobComparer)' C:\Users\youngs\Documents\Visual Studio 2012\Projects\SOERequestProgress\SOERequestProgress\SOERequestProgress\JobObject.cs 9 11 SOERequestProgress
Error 2 'SOERequestProgress.JobObject' does not implement interface member 'System.Collections.Generic.IEqualityComparer<SOERequestProgress.JobComparer>.Equals(SOERequestProgress.JobComparer, SOERequestProgress.JobComparer)' C:\Users\youngs\Documents\Visual Studio 2012\Projects\SOERequestProgress\SOERequestProgress\SOERequestProgress\JobObject.cs 9 11 SOERequestProgress
更新 3: 好吧,我几乎要认输了,因为我似乎无法理解 IEqualityComparer 是如何工作的……而且我又开始敲键盘了 >.<
下面的代码现在在 CompletedJobsArray 中获得了 72 个作业,这相当于 24 个作业重复了 3 次!!!
private static void updateCompleted(int index, XmlParser xmlParser)
{
var jobName = xmlParser.getElementAttrValue(index, "Name");
var jobDate = xmlParser.getElementAttrValue(index, "DateTime");
// Commented line below has same affect as uncommented line, except it always returns true....
//var CompletedJobs = Program.CompletedJobsArray.Distinct(new JobComparer(x => new { x.jobObjectName, x.jobObjectDateTime }));
var CompletedJobs = Program.CompletedJobsArray.Find(x=>x.Equals(jobDate));
if (CompletedJobs == null)
{
JobObject newCompletedJob = CreateJob("Completed", index, xmlParser);
Program.CompletedJobsArray.Add(newCompletedJob);
}
else
{
// Code below causes issues with IEnumerable
//UpdateJob("Completed", index, CompletedJobs, xmlParser);
}
}
我有 24 个作业,但是当对这些作业文件的 3 个位置进行轮询时,我只需要将 24 个唯一的作业添加到数组中....我做错了什么?
更新 4重复仍然出现...但不在列表中...
好的,再深入一点,我发现我的列表包含正确数量的作业,没有出现重复。
但是,当新工作进入列表时,程序的输出(在 UI 中)会针对某些工作而不是所有工作进行复制。
我似乎无法确定其中的原因,因为我试图让事情尽可能简单,并且正在使用一个列表(已确认按预期工作)并将我的输出生成到那里的列表视图。
这里是有问题的代码部分,
private void SortViews()
{
var Jobs = Program.JobsArray;
Program.JobsArray.Sort((a, b) => String.Compare(a.jobObjectDateTime, b.jobObjectDateTime));
if (Jobs.Count >= 0)
{
foreach (JobObject Job in Jobs.Where(x => PENDING_STATES.Contains(x.jobObjectState)))
{
if (Job.inPendingView && Job.inPendingView)
{
Job.inCompletedView = false;
CompletedJobsView.Items.Remove(PendingJobsView.FindItemWithText(Job.jobObjectName));
Job.inActiveView = false;
ActiveJobsView.Items.Remove(ActiveJobsView.FindItemWithText(Job.jobObjectName));
ListViewItem item = PendingJobsView.FindItemWithText(Job.jobObjectDateTime);
item.SubItems[0].Text = Job.jobObjectName;
item.SubItems[1].Text = Convert.ToString(Job.jobObjectPriority);
item.SubItems[2].Text = Job.jobObjectDiscName;
item.SubItems[3].Text = Convert.ToString(Job.jobObjectRequested);
item.SubItems[4].Text = Job.jobObjectDateTime;
}
else
{
string[] brr = new string[6];
ListViewItem item;
brr[0] = Job.jobObjectName;
brr[1] = Convert.ToString(Job.jobObjectPriority);
brr[2] = Job.jobObjectDiscName;
brr[3] = Convert.ToString(Job.jobObjectRequested);
brr[4] = Job.jobObjectDateTime;
item = new ListViewItem(brr);
item.ForeColor = Color.Blue;
PendingJobsView.Items.Add(item);
Job.inPendingView = true;
Job.inActiveView = false;
Job.inCompletedView = false;
Job.isActive = false;
Job.isCompleted = false;
Job.isPending = true;
}
}
foreach (JobObject Job in Jobs.Where(x => ACTIVE_STATES.Contains(x.jobObjectState)))
{
if (Job.isActive && Job.inActiveView)
{
Job.inPendingView = false;
PendingJobsView.Items.Remove(PendingJobsView.FindItemWithText(Job.jobObjectName));
Job.inCompletedView = false;
CompletedJobsView.Items.Remove(CompletedJobsView.FindItemWithText(Job.jobObjectDateTime));
//ActiveJobsView.Refresh();
ListViewItem item = ActiveJobsView.FindItemWithText(Job.jobObjectName);
item.SubItems[0].Text = Job.jobObjectName;
item.SubItems[1].Text = Convert.ToString(Job.jobObjectPriority);
item.SubItems[2].Text = Job.jobObjectDiscName;
item.SubItems[3].Text = Convert.ToString(Job.jobObjectCompleted) + " of " + Convert.ToString(Job.jobObjectRequested);
item.SubItems[4].Text = Convert.ToString(Job.jobObjectFailed);
item.SubItems[5].Text = Convert.ToString(Job.percentComplete) + "%";
item.SubItems[6].Text = Convert.ToString(Job.jobObjectState);
}
else
{
Job.isActive = true;
string[] crr = new string[7];
ListViewItem item;
crr[0] = Job.jobObjectName;
crr[1] = Convert.ToString(Job.jobObjectPriority);
crr[2] = Job.jobObjectDiscName;
crr[3] = Convert.ToString(Job.jobObjectCompleted) + " of " + Convert.ToString(Job.jobObjectRequested);
crr[4] = Convert.ToString(Job.jobObjectFailed);
crr[5] = Convert.ToString(Job.percentComplete) + "%";
crr[6] = Convert.ToString(Job.jobObjectState);
item = new ListViewItem(crr);
item.ForeColor = Color.DarkOrange;
ActiveJobsView.Items.Add(item);
Job.inActiveView = true;
Job.inPendingView = false;
Job.inCompletedView = false;
Job.isActive = true;
Job.isCompleted = false;
Job.isPending = false;
}
}
foreach (JobObject Job in Jobs.Where(x => COMPLETED_STATES.Contains(x.jobObjectState)))
{
if (Job.isCompleted && Job.inCompletedView)
{
Job.inPendingView = false;
PendingJobsView.Items.Remove(PendingJobsView.FindItemWithText(Job.jobObjectName));
Job.inActiveView = false;
ActiveJobsView.Items.Remove(ActiveJobsView.FindItemWithText(Job.jobObjectName));
//CompletedJobsView.Refresh();
ListViewItem item = CompletedJobsView.FindItemWithText(Job.jobObjectName);
item.SubItems[0].Text = Job.jobObjectName;
item.SubItems[1].Text = Job.jobObjectDiscName;
item.SubItems[2].Text = Convert.ToString(Job.jobObjectCompleted) + " of " + Convert.ToString(Job.jobObjectRequested);
item.SubItems[3].Text = Convert.ToString(Job.jobObjectFailed);
item.SubItems[4].Text = Convert.ToString(Job.jobObjectState);
item.SubItems[5].Text = Job.jobObjectDateTime;
}
else
{
string[] arr = new string[6];
ListViewItem item;
arr[0] = Job.jobObjectName;
arr[1] = Job.jobObjectDiscName;
arr[2] = Convert.ToString(Job.jobObjectCompleted) + " of " + Convert.ToString(Job.jobObjectRequested);
arr[3] = Convert.ToString(Job.jobObjectFailed);
arr[4] = Convert.ToString(Job.jobObjectState);
arr[5] = Job.jobObjectDateTime;
item = new ListViewItem(arr);
if (Job.jobObjectState == "ERROR")
{
item.ForeColor = Color.Firebrick;
}
if (Job.jobObjectState == "COMPLETE")
{
item.ForeColor = Color.Green;
}
CompletedJobsView.Items.Add(item);
Job.inCompletedView = true;
Job.inPendingView = false;
Job.inActiveView = false;
Job.isCompleted = true;
Job.isActive = false;
Job.isPending = false;
}
}
}
}
任何想法是什么导致了这种行为?
再次感谢:)
为您的 PendingJobs 对象定义自定义 IEqualityComparer。然后,您可以根据需要轻松调整比较,并比较所有字段或字段的子集。然后你可以创建一个 HashSet 并将其提供给这个比较器。如果更好,您也可以使用字典,因为它也接受 IEqualityComparer。
与其让代码复杂化,不如在这里使用单个列表更容易。
List<JobObject> jobs = new List<JobObject>();
// Code that adds / updates a job
var newJob = CreateJob(state);
// The equality comparer should check for a job name and job date of two jobs to ensure the uniqueness
var existingJob = jobs.Find(x => x.Equals(x, newJob));
if(existingJob != null)
{
// Update your existing job
// If you want to calulate the time interval between job start till end, you can have more properties
// indicating the jobPendingStartTime, jobActiveStartTime etc.
}
else
{
jobs.Add(newJob);
}
// Code to find jobs
var pendingJobs = jobs.Where(x => PENDING_STATES.contains(x.jobObjectState));
var activeJobs = jobs.Where(x => ACTIVE_STATES.contains(x.jobObjectState));
var completedJobs = jobs.Where(x => COMPLETED_STATES.contains(x.jobObjectState));
// Bind to different list views
IEqualityComparer 实现:
public class JobObject
{
public string Name { get; set; }
public string Date { get; set; }
//Other properties
}
public class JobObjectComparer : IEqualityComparer<JobObject>
{
public bool Equals(JobObject x, JobObject y)
{
return x.Name == y.Name &&
x.Date == y.Date;
}
public int GetHashCode(JobObject job)
{
return job.GetHashCode();
}
// Then use it as follows.
// JobObject newJob = CreateJob();
// var comparer = new JobObjectComparer();
// var existingJob = jobsArray.Find(x => comparer.Equals(x, newJob));
// if(existingJob != null)
// {
// Job Exists
// }
}
如果有帮助请告诉我。
我已经尝试了一遍又一遍,但我现在只是在兜圈子。
我在外部存储了一组大部分独特的作业文件,我从这些文件中收集信息并通过列表将其存储在我的程序中。
有时,我有两个同名的作业文件(不幸的是,这是不可避免的)但包含不同的数据(其中最独特的是 DateTime 字符串)。
我的程序通过轮询这些文件的 3 个位置(所有位置都是镜像的,因此每个位置都存在相同的数据)来工作,收集信息,然后根据当前状态将信息输出到 ListView window每个作业文件。
我遇到的问题是防止将重复的条目添加到我的列表中,导致列表中只有 23 个作业文件有 69 个对象。
为了避免在列表视图中出现重复输出,我尝试使用字典来存储基于作业文件名的已知键,但是当到达与现有条目同名的第二个文件时,这会失败。结果我在列表中只看到 21 个工作,本应包含 23 个工作(我也抛出了异常)。我通过在第二个条目的末尾附加一些内容来解决这个问题,但这并不理想,后来发现会导致其他并发症。
有什么想法吗?
这是我目前所拥有的。
public List<object> PendingJobsArray = new List<object>();
public List<object> ActiveJobsArray = new List<object>();
public List<object> CompletedJobsArray = new List<object>();
public JobObject PendingJobs = new JobObject();
public JobObject ActiveJobs = new JobObject();
public JobObject CompletedJobs = new JobObject();
private void updatePending(int index, XmlParser xmlParser)
{
if (Program.PendingJobsArray.Count == 0)
{
Program.KnownPendingJobs.Add(xmlParser.getElementAttrValue(index, "Name"), xmlParser.getElementAttrValue(index, "DateTime"));
JobObject PendingJobs = new JobObject();
PendingJobs.jobObjectName = xmlParser.getElementAttrValue(index, "Name");
PendingJobs.jobObjectState = xmlParser.getElementAttrValue(index, "State");
PendingJobs.jobObjectDiscName = xmlParser.getElementAttrValue(index, "DiscName");
PendingJobs.jobObjectPercentComplete = Convert.ToInt32(xmlParser.getElementAttrValue(index, "TotalPercentComplete"));
PendingJobs.jobObjectDateTime = xmlParser.getElementAttrValue(index, "DateTime");
PendingJobs.jobObjectRequested = Convert.ToInt32(xmlParser.getElementAttrValue(index, "Requested"));
PendingJobs.jobObjectCompleted = Convert.ToInt32(xmlParser.getElementAttrValue(index, "Completed"));
PendingJobs.jobObjectFailed = Convert.ToInt32(xmlParser.getElementAttrValue(index, "Failed"));
PendingJobs.jobObjectPriority = Convert.ToInt32(xmlParser.getElementAttrValue(index, "Priority"));
// PendingJobs.jobObjectServer = xmlParser.getElementAttrValue("Server");
// PendingJobs.jobObjectOperation = xmlParser.getElementAttrValue("Operation");
Program.PendingJobsArray.Add(PendingJobs);
}
else if (Program.KnownPendingJobs.ContainsKey(xmlParser.getElementAttrValue(index, "Name")))
{
for (int i = 0; i < Program.PendingJobsArray.Count; ++i)
{
if (xmlParser.getElementAttrValue(index, "State") == "Write")
{
Program.PendingJobsArray.RemoveAt(index);
}
else
{
PendingJobs.jobObjectName = xmlParser.getElementAttrValue(index, "Name");
PendingJobs.jobObjectState = xmlParser.getElementAttrValue(index, "State");
PendingJobs.jobObjectDiscName = xmlParser.getElementAttrValue(index, "DiscName");
PendingJobs.jobObjectPercentComplete = Convert.ToInt32(xmlParser.getElementAttrValue(index, "TotalPercentComplete"));
PendingJobs.jobObjectDateTime = xmlParser.getElementAttrValue(index, "DateTime");
PendingJobs.jobObjectRequested = Convert.ToInt32(xmlParser.getElementAttrValue(index, "Requested"));
PendingJobs.jobObjectCompleted = Convert.ToInt32(xmlParser.getElementAttrValue(index, "Completed"));
PendingJobs.jobObjectFailed = Convert.ToInt32(xmlParser.getElementAttrValue(index, "Failed"));
PendingJobs.jobObjectPriority = Convert.ToInt32(xmlParser.getElementAttrValue(index, "Priority"));
Program.PendingJobsArray[index] = PendingJobs;
}
}
}
else if (Program.KnownPendingJobs.ContainsKey(xmlParser.getElementAttrValue(index, "Name")))
}
else
{
Program.KnownPendingJobs.Add(xmlParser.getElementAttrValue(index, "Name"), xmlParser.getElementAttrValue(index, "DateTime"));
JobObject PendingJobs = new JobObject();
PendingJobs.jobObjectName = xmlParser.getElementAttrValue(index, "Name");
PendingJobs.jobObjectState = xmlParser.getElementAttrValue(index, "State");
PendingJobs.jobObjectDiscName = xmlParser.getElementAttrValue(index, "DiscName");
PendingJobs.jobObjectPercentComplete = Convert.ToInt32(xmlParser.getElementAttrValue(index, "TotalPercentComplete"));
PendingJobs.jobObjectDateTime = xmlParser.getElementAttrValue(index, "DateTime");
PendingJobs.jobObjectRequested = Convert.ToInt32(xmlParser.getElementAttrValue(index, "Requested"));
PendingJobs.jobObjectCompleted = Convert.ToInt32(xmlParser.getElementAttrValue(index, "Completed"));
PendingJobs.jobObjectFailed = Convert.ToInt32(xmlParser.getElementAttrValue(index, "Failed"));
PendingJobs.jobObjectQuantity = xmlParser.getElementAttrValue(index, "Quantity");
PendingJobs.jobObjectPriority = Convert.ToInt32(xmlParser.getElementAttrValue(index, "Priority"));
//PendingJobs.jobObjectServer = xmlParser.getElementAttrValue("Server");
//PendingJobs.jobObjectOperation = xmlParser.getElementAttrValue("Operation");
Program.PendingJobsArray.Add(PendingJobs);
}
}
更新 代码基于下面的评论,
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Collections;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
using System.Windows.Forms;
namespace SOERequestProgress
{
class XmlSplitter
{
// Declarations
private static string[] COMPLETED_STATES = new string[2]
{
"ERROR",
"COMPLETE"
};
private static string[] PENDING_STATES = new string[1]
{
"PENDING"
};
private static string[] ACTIVE_STATES = new string[1]
{
"PAUSED"
};
private const string STATE_ERROR = "ERROR";
private const string STATE_COMPLETE = "COMPLETE";
private const string STATE_PENDING = "PENDING";
private const string STATE_ACTIVE = "PAUSED";
public static JobObject PendingJobs = new JobObject();
public static JobObject CompletedJobs = new JobObject();
public static JobObject ActiveJobs = new JobObject();
//public static JobObject x;
public static int intI = 0;
// Add additional info from parsed XML (Currently only "Name" and "State" are collected.
public static void splitXml(string JobList, out List<JobObject> Active, out List<JobObject> Completed, out List<JobObject> Pending)
{
bool flag = false;
int num1 = 0;
string text = "";
string xmlString = "";
while (!flag)
{
if (num1 < 3)
{
try
{
xmlString = JobList;
flag = true;
}
catch (Exception ex)
{
++num1;
text = text + "Try (" + num1.ToString() + ") " + ex.Message + "\n";
Thread.Sleep(1000);
}
}
else
break;
}
if (!flag)
{
int num2 = (int)MessageBox.Show(text, "Job list error", MessageBoxButtons.OK, MessageBoxIcon.Hand);
}
else
{
XmlParser xmlParser = new XmlParser();
if (!xmlParser.setXmlString(xmlString))
{
int num3 = (int)MessageBox.Show("Error parsing job list server response.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Hand);
flag = false;
}
else
{
int num3 = xmlParser.setElementName("Job");
for (int index = 0; index < num3; ++index)
{
string elementAttrValue1 = xmlParser.getElementAttrValue(index, "Name");
string elementAttrValue2 = xmlParser.getElementAttrValue(index, "State");
if (isState(elementAttrValue2, PENDING_STATES))
{
updatePending(index, xmlParser);
}
else if (isState(elementAttrValue2, COMPLETED_STATES))
{
updateCompleted(index, xmlParser);
}
else //if (isState(elementAttrValue2, ACTIVE_STATES))
{
updateActive(index, xmlParser);
}
}
}
}
Active = Program.ActiveJobsArray;
Completed = Program.CompletedJobsArray;
Pending = Program.PendingJobsArray;
}
private static void updatePending(int index, XmlParser xmlParser)
{
var jobName = xmlParser.getElementAttrValue(index, "Name");
var pendingJob = Program.PendingJobsArray.Find(x => x.jobObjectName == jobName);
if (pendingJob == null)
{
JobObject newPendingJob = CreateJob("Pending", index, xmlParser);
Program.PendingJobsArray.Add(newPendingJob);
}
}
private static void updateCompleted(int index, XmlParser xmlParser)
{
var jobName = xmlParser.getElementAttrValue(index, "Name");
var CompletedJob = Program.CompletedJobsArray.Find(x => x.jobObjectName == jobName);
if (CompletedJob == null)
{
JobObject newCompletedJob = CreateJob("Completed", index, xmlParser);
Program.CompletedJobsArray.Add(newCompletedJob);
}
}
private static void updateActive(int index, XmlParser xmlParser)
{
var jobName = xmlParser.getElementAttrValue(index, "Name");
var activeJob = Program.ActiveJobsArray.Find(x => x.jobObjectName == jobName);
if (activeJob == null)
{
JobObject newActiveJob = CreateJob("Active", index, xmlParser);
Program.ActiveJobsArray.Add(newActiveJob);
}
}
private static JobObject CreateJob(string jobType, int index, XmlParser xmlParser)
{
if (jobType == "Pending")
{
JobObject x = new JobObject();
x.jobObjectName = xmlParser.getElementAttrValue(index, "Name");
x.jobObjectState = xmlParser.getElementAttrValue(index, "State");
x.jobObjectDiscName = xmlParser.getElementAttrValue(index, "DiscName");
x.jobObjectPercentComplete = Convert.ToInt32(xmlParser.getElementAttrValue(index, "TotalPercentComplete"));
x.jobObjectDateTime = xmlParser.getElementAttrValue(index, "DateTime");
x.jobObjectRequested = Convert.ToInt32(xmlParser.getElementAttrValue(index, "Requested"));
x.jobObjectCompleted = Convert.ToInt32(xmlParser.getElementAttrValue(index, "Completed"));
x.jobObjectFailed = Convert.ToInt32(xmlParser.getElementAttrValue(index, "Failed"));
x.jobObjectQuantity = xmlParser.getElementAttrValue(index, "Quantity");
x.jobObjectPriority = Convert.ToInt32(xmlParser.getElementAttrValue(index, "Priority"));
//x.jobObjectServer = xmlParser.getElementAttrValue("Server");
//x.jobObjectOperation = xmlParser.getElementAttrValue("Operation");
return x;
}
if (jobType == "Completed")
{
JobObject x = new JobObject();
x.jobObjectName = xmlParser.getElementAttrValue(index, "Name");
x.jobObjectState = xmlParser.getElementAttrValue(index, "State");
x.jobObjectDiscName = xmlParser.getElementAttrValue(index, "DiscName");
x.jobObjectPercentComplete = Convert.ToInt32(xmlParser.getElementAttrValue(index, "TotalPercentComplete"));
x.jobObjectDateTime = xmlParser.getElementAttrValue(index, "DateTime");
x.jobObjectRequested = Convert.ToInt32(xmlParser.getElementAttrValue(index, "Requested"));
x.jobObjectCompleted = Convert.ToInt32(xmlParser.getElementAttrValue(index, "Completed"));
x.jobObjectFailed = Convert.ToInt32(xmlParser.getElementAttrValue(index, "Failed"));
//x.jobObjectServer = xmlParser.getElementAttrValue("Server");
//x.jobObjectOperation = xmlParser.getElementAttrValue("Operation");
return x;
}
if (jobType == "Active")
{
JobObject x = new JobObject();
x.jobObjectName = xmlParser.getElementAttrValue(index, "Name");
x.jobObjectState = xmlParser.getElementAttrValue(index, "State");
x.jobObjectDiscName = xmlParser.getElementAttrValue(index, "DiscName");
x.jobObjectPercentComplete = Convert.ToInt32(xmlParser.getElementAttrValue(index, "TotalPercentComplete"));
x.jobObjectDateTime = xmlParser.getElementAttrValue(index, "DateTime");
x.jobObjectRequested = Convert.ToInt32(xmlParser.getElementAttrValue(index, "Requested"));
x.jobObjectCompleted = Convert.ToInt32(xmlParser.getElementAttrValue(index, "Completed"));
x.jobObjectFailed = Convert.ToInt32(xmlParser.getElementAttrValue(index, "Failed"));
x.jobObjectPriority = Convert.ToInt32(xmlParser.getElementAttrValue(index, "Priority"));
//x.jobObjectServer = xmlParser.getElementAttrValue("Server");
//x.jobObjectOperation = xmlParser.getElementAttrValue("Operation");
return x;
}
return null;
}
private static bool isState(string jobState, string[] knownStates)
{
bool flag = false;
for (int index = 0; index < knownStates.Length; ++index)
{
if (jobState.ToUpper().Equals(knownStates[index]))
{
flag = true;
//break;
}
}
return flag;
}
}
}
这个新代码似乎不正确,我看不出它在哪里失败,但它在几秒钟内用 200 多个条目填充了我的 CompletedJobArray。
我的 ListView 仅显示 1 个条目(针对唯一作业进行过滤)并且在检查数组的内容时,它为所有 200 多个条目添加了相同的 jobObject。
谁能找出这段代码中的缺陷?
更新 2: IEqualityComparer 已实施,遇到更多错误。
下面的代码是Equals和GetHashCode的实现。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace SOERequestProgress
{
class JobComparer : IEqualityComparer<JobObject>
{
public JobComparer(Func<JobObject, object> keySelector)
{
KeySelector = keySelector;
}
public bool Equals(JobObject x, JobObject y)
{
return KeySelector(x).Equals(KeySelector(y));
//x.jobObjectName == y.jobObjectName &&
//x.jobObjectDateTime == y.jobObjectDateTime;
}
public int GetHashCode(JobObject job)
{
return KeySelector(job).GetHashCode();
}
}
}
此实现抛出以下错误,
Error 1 'SOERequestProgress.JobObject' does not implement interface member 'System.Collections.Generic.IEqualityComparer<SOERequestProgress.JobComparer>.GetHashCode(SOERequestProgress.JobComparer)' C:\Users\youngs\Documents\Visual Studio 2012\Projects\SOERequestProgress\SOERequestProgress\SOERequestProgress\JobObject.cs 9 11 SOERequestProgress
Error 2 'SOERequestProgress.JobObject' does not implement interface member 'System.Collections.Generic.IEqualityComparer<SOERequestProgress.JobComparer>.Equals(SOERequestProgress.JobComparer, SOERequestProgress.JobComparer)' C:\Users\youngs\Documents\Visual Studio 2012\Projects\SOERequestProgress\SOERequestProgress\SOERequestProgress\JobObject.cs 9 11 SOERequestProgress
更新 3: 好吧,我几乎要认输了,因为我似乎无法理解 IEqualityComparer 是如何工作的……而且我又开始敲键盘了 >.<
下面的代码现在在 CompletedJobsArray 中获得了 72 个作业,这相当于 24 个作业重复了 3 次!!!
private static void updateCompleted(int index, XmlParser xmlParser)
{
var jobName = xmlParser.getElementAttrValue(index, "Name");
var jobDate = xmlParser.getElementAttrValue(index, "DateTime");
// Commented line below has same affect as uncommented line, except it always returns true....
//var CompletedJobs = Program.CompletedJobsArray.Distinct(new JobComparer(x => new { x.jobObjectName, x.jobObjectDateTime }));
var CompletedJobs = Program.CompletedJobsArray.Find(x=>x.Equals(jobDate));
if (CompletedJobs == null)
{
JobObject newCompletedJob = CreateJob("Completed", index, xmlParser);
Program.CompletedJobsArray.Add(newCompletedJob);
}
else
{
// Code below causes issues with IEnumerable
//UpdateJob("Completed", index, CompletedJobs, xmlParser);
}
}
我有 24 个作业,但是当对这些作业文件的 3 个位置进行轮询时,我只需要将 24 个唯一的作业添加到数组中....我做错了什么?
更新 4重复仍然出现...但不在列表中...
好的,再深入一点,我发现我的列表包含正确数量的作业,没有出现重复。
但是,当新工作进入列表时,程序的输出(在 UI 中)会针对某些工作而不是所有工作进行复制。
我似乎无法确定其中的原因,因为我试图让事情尽可能简单,并且正在使用一个列表(已确认按预期工作)并将我的输出生成到那里的列表视图。
这里是有问题的代码部分,
private void SortViews()
{
var Jobs = Program.JobsArray;
Program.JobsArray.Sort((a, b) => String.Compare(a.jobObjectDateTime, b.jobObjectDateTime));
if (Jobs.Count >= 0)
{
foreach (JobObject Job in Jobs.Where(x => PENDING_STATES.Contains(x.jobObjectState)))
{
if (Job.inPendingView && Job.inPendingView)
{
Job.inCompletedView = false;
CompletedJobsView.Items.Remove(PendingJobsView.FindItemWithText(Job.jobObjectName));
Job.inActiveView = false;
ActiveJobsView.Items.Remove(ActiveJobsView.FindItemWithText(Job.jobObjectName));
ListViewItem item = PendingJobsView.FindItemWithText(Job.jobObjectDateTime);
item.SubItems[0].Text = Job.jobObjectName;
item.SubItems[1].Text = Convert.ToString(Job.jobObjectPriority);
item.SubItems[2].Text = Job.jobObjectDiscName;
item.SubItems[3].Text = Convert.ToString(Job.jobObjectRequested);
item.SubItems[4].Text = Job.jobObjectDateTime;
}
else
{
string[] brr = new string[6];
ListViewItem item;
brr[0] = Job.jobObjectName;
brr[1] = Convert.ToString(Job.jobObjectPriority);
brr[2] = Job.jobObjectDiscName;
brr[3] = Convert.ToString(Job.jobObjectRequested);
brr[4] = Job.jobObjectDateTime;
item = new ListViewItem(brr);
item.ForeColor = Color.Blue;
PendingJobsView.Items.Add(item);
Job.inPendingView = true;
Job.inActiveView = false;
Job.inCompletedView = false;
Job.isActive = false;
Job.isCompleted = false;
Job.isPending = true;
}
}
foreach (JobObject Job in Jobs.Where(x => ACTIVE_STATES.Contains(x.jobObjectState)))
{
if (Job.isActive && Job.inActiveView)
{
Job.inPendingView = false;
PendingJobsView.Items.Remove(PendingJobsView.FindItemWithText(Job.jobObjectName));
Job.inCompletedView = false;
CompletedJobsView.Items.Remove(CompletedJobsView.FindItemWithText(Job.jobObjectDateTime));
//ActiveJobsView.Refresh();
ListViewItem item = ActiveJobsView.FindItemWithText(Job.jobObjectName);
item.SubItems[0].Text = Job.jobObjectName;
item.SubItems[1].Text = Convert.ToString(Job.jobObjectPriority);
item.SubItems[2].Text = Job.jobObjectDiscName;
item.SubItems[3].Text = Convert.ToString(Job.jobObjectCompleted) + " of " + Convert.ToString(Job.jobObjectRequested);
item.SubItems[4].Text = Convert.ToString(Job.jobObjectFailed);
item.SubItems[5].Text = Convert.ToString(Job.percentComplete) + "%";
item.SubItems[6].Text = Convert.ToString(Job.jobObjectState);
}
else
{
Job.isActive = true;
string[] crr = new string[7];
ListViewItem item;
crr[0] = Job.jobObjectName;
crr[1] = Convert.ToString(Job.jobObjectPriority);
crr[2] = Job.jobObjectDiscName;
crr[3] = Convert.ToString(Job.jobObjectCompleted) + " of " + Convert.ToString(Job.jobObjectRequested);
crr[4] = Convert.ToString(Job.jobObjectFailed);
crr[5] = Convert.ToString(Job.percentComplete) + "%";
crr[6] = Convert.ToString(Job.jobObjectState);
item = new ListViewItem(crr);
item.ForeColor = Color.DarkOrange;
ActiveJobsView.Items.Add(item);
Job.inActiveView = true;
Job.inPendingView = false;
Job.inCompletedView = false;
Job.isActive = true;
Job.isCompleted = false;
Job.isPending = false;
}
}
foreach (JobObject Job in Jobs.Where(x => COMPLETED_STATES.Contains(x.jobObjectState)))
{
if (Job.isCompleted && Job.inCompletedView)
{
Job.inPendingView = false;
PendingJobsView.Items.Remove(PendingJobsView.FindItemWithText(Job.jobObjectName));
Job.inActiveView = false;
ActiveJobsView.Items.Remove(ActiveJobsView.FindItemWithText(Job.jobObjectName));
//CompletedJobsView.Refresh();
ListViewItem item = CompletedJobsView.FindItemWithText(Job.jobObjectName);
item.SubItems[0].Text = Job.jobObjectName;
item.SubItems[1].Text = Job.jobObjectDiscName;
item.SubItems[2].Text = Convert.ToString(Job.jobObjectCompleted) + " of " + Convert.ToString(Job.jobObjectRequested);
item.SubItems[3].Text = Convert.ToString(Job.jobObjectFailed);
item.SubItems[4].Text = Convert.ToString(Job.jobObjectState);
item.SubItems[5].Text = Job.jobObjectDateTime;
}
else
{
string[] arr = new string[6];
ListViewItem item;
arr[0] = Job.jobObjectName;
arr[1] = Job.jobObjectDiscName;
arr[2] = Convert.ToString(Job.jobObjectCompleted) + " of " + Convert.ToString(Job.jobObjectRequested);
arr[3] = Convert.ToString(Job.jobObjectFailed);
arr[4] = Convert.ToString(Job.jobObjectState);
arr[5] = Job.jobObjectDateTime;
item = new ListViewItem(arr);
if (Job.jobObjectState == "ERROR")
{
item.ForeColor = Color.Firebrick;
}
if (Job.jobObjectState == "COMPLETE")
{
item.ForeColor = Color.Green;
}
CompletedJobsView.Items.Add(item);
Job.inCompletedView = true;
Job.inPendingView = false;
Job.inActiveView = false;
Job.isCompleted = true;
Job.isActive = false;
Job.isPending = false;
}
}
}
}
任何想法是什么导致了这种行为?
再次感谢:)
为您的 PendingJobs 对象定义自定义 IEqualityComparer。然后,您可以根据需要轻松调整比较,并比较所有字段或字段的子集。然后你可以创建一个 HashSet 并将其提供给这个比较器。如果更好,您也可以使用字典,因为它也接受 IEqualityComparer。
与其让代码复杂化,不如在这里使用单个列表更容易。
List<JobObject> jobs = new List<JobObject>();
// Code that adds / updates a job
var newJob = CreateJob(state);
// The equality comparer should check for a job name and job date of two jobs to ensure the uniqueness
var existingJob = jobs.Find(x => x.Equals(x, newJob));
if(existingJob != null)
{
// Update your existing job
// If you want to calulate the time interval between job start till end, you can have more properties
// indicating the jobPendingStartTime, jobActiveStartTime etc.
}
else
{
jobs.Add(newJob);
}
// Code to find jobs
var pendingJobs = jobs.Where(x => PENDING_STATES.contains(x.jobObjectState));
var activeJobs = jobs.Where(x => ACTIVE_STATES.contains(x.jobObjectState));
var completedJobs = jobs.Where(x => COMPLETED_STATES.contains(x.jobObjectState));
// Bind to different list views
IEqualityComparer 实现:
public class JobObject
{
public string Name { get; set; }
public string Date { get; set; }
//Other properties
}
public class JobObjectComparer : IEqualityComparer<JobObject>
{
public bool Equals(JobObject x, JobObject y)
{
return x.Name == y.Name &&
x.Date == y.Date;
}
public int GetHashCode(JobObject job)
{
return job.GetHashCode();
}
// Then use it as follows.
// JobObject newJob = CreateJob();
// var comparer = new JobObjectComparer();
// var existingJob = jobsArray.Find(x => comparer.Equals(x, newJob));
// if(existingJob != null)
// {
// Job Exists
// }
}
如果有帮助请告诉我。