我有一个C#数据表(// In ViewDidLoad
NotificationCenter.default.addObserver(self, selector: #selector(?MyViewController.keyboardDidChange), name: UIResponder.keyboardWillChangeFrameNotification, object: nil)
@objc func keyboardDidChange(notification: Notification) {
let userInfo = notification.userInfo! as [AnyHashable: Any]
let endFrame = (userInfo[UIResponder.keyboardFrameEndUserInfoKey] as! NSValue).cgRectValue
let animationDuration = userInfo[UIResponder.keyboardAnimationDurationUserInfoKey] as! NSNumber
let animationCurve = userInfo[UIResponder.keyboardAnimationCurveUserInfoKey] as! NSNumber
bottomLayoutConstraint.constant = view.frame.height - endFrame.origin.y - view.safeAreaInsets.bottom // If your constraint is not defined as a safeArea constraint you might want to skip the last part.
// Prevents iPad undocked keyboard.
guard endFrame.height != 0, view.frame.height == endFrame.height + endFrame.origin.y else {
bottomLayoutConstraint.constant = 0
return
}
UIView.setAnimationCurve(UIView.AnimationCurve(rawValue: animationCurve.intValue)!)
UIView.animate(withDuration: animationDuration.doubleValue) {
self.view.layoutIfNeeded()
// Do additional tasks such as scrolling in a UICollectionView
}
}
),看起来像这样:
我有以下内存_dtOptions
:
select
我使用DataRow[] matchingRows = _dtOptions.Select("OptionID Like 'M00F????'");
而不是?
的原因是因为我只想匹配以*
开头的8个字符的记录。
问题:执行该行代码后M00F
为空。正如您在屏幕截图中看到的那样,它应该与很多记录匹配。
注意:当我对基础matchingRows
数据库运行此查询时,确实得到了预期的结果:Access
我在做什么错了?
答案 0 :(得分:1)
请尝试以下操作:
DataRow[] matchingRows = _dtOptions.AsEnumerable()
.Where(x => x.Field<string>("OptionID").Length == 8 &&
x.Field<string>("OptionID").StartsWith("M00F"))
.ToArray();
答案 1 :(得分:1)
出现问题是因为您将数据的存储方式(DataTable)与您要处理数据的方式(LINQ语句)以及可能要与结果进行处理的方式(在屏幕上显示)混合在一起?)
正确的方法是将这些问题分开:将数据在内部的保存方式与处理方式分开,并处理结果。
在您的示例中,您的数据是从DataTable
检索的,然后进行一些处理,并且可能会对结果进行某些处理。
如果将来您决定从列表,数据库或CSV文件或从Internet检索相似的数据,则您既不想更改处理部分,也不想更改执行操作的部分结果。
类似地,如果您重新排列DataTable中的列,或者以不同的方式命名它们,则您不希望弄乱处理过程。想一想您必须检查的所有代码和所有单元测试,仅因为重命名了一个列!
分开您的担忧有一些好处
因此,让我们分开关注吧!
首先,您需要一个类来表示保存在DataTable
中的数据。显然,这是IEnumerable
每一行中保存的DataTable
数据序列。像这样:
class MyData
{
public string OptionId {get; set;}
public string CatId {get; set;}
...
}
您还可以创建一个代表a DataRow containing one object of MyData
的类和一个代表a DataTable that contains a sequence of my DataRows
的类。
我个人认为这些类不会增加很多功能,因此我将为DataRow
和DataTable
写一些类似LINQ的扩展函数,这些函数会将您的表转换为一个可枚举的MyData
。参见Extension Methods demystified
// Convert a DataRow to a MyData:
public static MyData ToMyData(this DataRow row)
{
// Todo: check for null row
return new MyData
{
OptionId = row... // TODO implement
...
}
}
// Convert a sequence of DataRows to a sequence of MyData
public static IEnumerable<MyData> ToMyData(this IEnumerable<DataRow> rows)
{
// TODO: check for null rows
return rows.Select(row => row.ToMyData());
}
// Convert a DataTable to a sequence of MyData:
public static IEnumerable<MyData> ToMyData(this DataTable table)
{
// TODO: check for null table
return table.Rows.Cast<DataRow>().ToMyData();
}
因此,经过三个单行函数,您可以将DataTable转换为MyData序列,您可以使用LINQ:
DataTable table = ...
IEnumerable<MyData> myItems = table.ToMyData();
您只希望表中的MyData
对象具有OptionId
,长度为8,并且以字符串“ M00F”开头。
因为我消除了顾虑,所以实现非常简单:
var matchingData = myItems.Where(myItem => myItem.OptionId.Length == 8
&& myItem.OptionId.StartsWith("M00F"));
每个myItem都是MyData的对象。
如果您打算更频繁地使用MatchingData
这个概念,或者要记住,将来您可能想更改匹配的数据,例如以“ M11F”开头的数据,因此如果希望使此需求更好地可更改,可重用,可测试(无论如何,您现在就知道了演练),使其成为MyData
的扩展功能:
public static IEnumerable<MyData> WhereMatches(this IEnumerable<MyData> source)
{
// TODO: check for null source
return source.Where(item => item.OptionId.Length == 8
&& item.OptionId.StartsWith("M00F"));
}
看看测试这个有多容易!无需测试DataTables等。如果您的信息来自数据库,则此功能也将起作用。如果您需要匹配“ M11F”,只需进行少量更改
您在一份声明中的完整要求:
DataTable table = ...
var matchinData = table.ToMyData().WhereMatches();
一些旁注: