让我重新分析这个问题:
- 需要考虑的HTML情况:
- 简单标签:
<div>text</div>
- 带属性标签:
<a href="url">text</a>
- 自闭合标签:
<br/>
- 带属性的自闭合标签:
<img src="img.jpg"/>
- 需要排除的markdown链接格式:
- 专指
<https://...>
这种格式
- 特征是 < 后紧跟 http 或 https
- 当前正则的问题:
<\s*([a-zA-Z]+)[^>]*>.*</\s*\1\s*>|<\s*([a-zA-Z]+)[^>]*/>
它会匹配所有以 < 开头,包含字母的内容,这就导致了误匹配
- 改进思路:
- 排除标签名以
http(s)
开头的情况
- 使用负向预查
(?!http(s)?)
- 放在标签名匹配的开始处
- 优化后的正则表达式:
<\s*(?!https?)([a-zA-Z]+)[^>]*>.*</\s*\1\s*>|<\s*(?!https?)([a-zA-Z]+)[^>]*/>
- 验证效果:
<div>text</div>
✓ 匹配
<a href="url">text</a>
✓ 匹配
<br/>
✓ 匹配
<img src="img.jpg"/>
✓ 匹配
<https://abc.com/>
✗ 不匹配
这个解决方案:
- 保留了原有正则的基本结构
- 只添加了对标签名的限制条件
- 能正确处理各种HTML标签
- 成功排除markdown格式的URL
建议修改代码为:
public static bool IsHtml(this string input)
{
return Regex.IsMatch(input,
@"<\s*(?!https?)([a-zA-Z]+)[^>]*>.*</\s*\1\s*>|<\s*(?!https?)([a-zA-Z]+)[^>]*/>",
RegexOptions.Singleline | RegexOptions.IgnoreCase);
}
Flutter’s didChangeDependencies
lifecycle method is a common source of redundant network requests and data reloads. Based on real-world code examples from a notes app, let’s explore why this happens and how to fix it.
When Does didChangeDependencies
Trigger?
didChangeDependencies
in Flutter triggers in these scenarios:
- Immediately after initState()
- When an InheritedWidget ancestor changes
- When the widget's dependencies change
In the provided HomePage
code:
@override
void didChangeDependencies() {
super.didChangeDependencies();
navigateToPage(currentPageNumber); // ❌ Unconditional call
}
This causes duplicate API calls every time the event being triggerd.
The Fix: Initialize-Once Flag
class HomePageState extends State<HomePage> {
...
bool _isInitialized = false; // Add flag
@override
void didChangeDependencies() {
super.didChangeDependencies();
if (!_isInitialized) {
_isInitialized = true;
navigateToPage(currentPageNumber); // ✅ Only first load
}
}
...
}
Why This Works
- First Load: Initializes data once
- Subsequent Route Changes: Skips reload unless explicitly refreshed
- Memory Efficiency: Prevents duplicate API calls (evident from the
NotesService
cache-less implementation)
Key Takeaways
didChangeDependencies
isn’t just for initialization
- Always guard data-loading logic with flags
... skipping 1000 words about bad solutions ...
The Clean/Best Solution
Here's a pattern that elegantly handles this situation:
public void RefreshGrid()
{
// 1. Store the current entity before refresh
SomeEntity currentEntity = null;
if (dataGrid.ActiveRow != null)
{
currentEntity = dataGrid.ActiveRow.ListObject as SomeEntity;
}
// 2. Refresh the grid
dataGrid.RefreshData();
// 3. Restore selection using entity ID
if (currentEntity != null)
{
dataGrid.ActiveRow = dataGrid.Rows.FirstOrDefault(r =>
((SomeEntity)r.ListObject).Id == currentEntity.Id);
}
}
Why This Pattern Is Best Practice
- Type Safety: Using the strongly-typed entity object instead of raw values
- Identity-Based: Uses unique IDs instead of volatile row positions
- Null-Safe: Handles cases where no row is selected
- Concise: LINQ makes the code readable and maintainable
- Reliable: Works even if data order changes or rows are filtered
Wen working with QuickFix/n ibrary , efficient logging is crucial for troubleshooting. Here's how to implement a custom logging solution that routes QuickFix logs through NLog to your ELK stack.
Key Components
- NLogAdapter: A custom adapter that implements QuickFix's
ILog
interface:
public class NLogAdapter(ILogger logger) : ILog
{
private const string HeartbeatPattern = @"\x0135=0\x01";
private static readonly Regex HeartbeatRegex = new(HeartbeatPattern, RegexOptions.Compiled);
private static bool IsHeartBeat(string message) => HeartbeatRegex.IsMatch(message);
public void OnIncoming(string message)
{
if (!IsHeartBeat(message))
{
logger.Info("Incoming: {Message}", message);
}
}
// ... other implementations
}
- NLogQuickFixLogFactory: A factory class to create log instances:
public class NLogQuickFixLogFactory(ILog logger) : ILogFactory
{
public ILog Create(SessionID sessionId) => logger;
public ILog CreateNonSessionLog() => logger;
}
Implementation Steps
- Register Dependencies in your DI container:
builder.Services.AddSingleton<NLog.ILogger>(_ => LogManager.GetCurrentClassLogger());
builder.Services.AddSingleton<ILog, NLogAdapter>();
builder.Services.AddSingleton<ILogFactory, NLogQuickFixLogFactory>();
- Configure QuickFix to use the custom logger:
var initiator = new SocketInitiator(
clientApp,
storeFactory,
sessionSettings,
new NLogQuickFixLogFactory(quickfixLogger) // Use custom logger injeted by ILog here
);
Key Features
- Heartbeat Filtering: Reduces log noise by filtering out FIX heartbeat messages
- Structured Logging: Uses NLog's structured logging format for better parsing in ELK
- Separation of Concerns: Cleanly separates QuickFix logging from application logging
Benefits
- Centralized logging in ELK stack
- Better debugging apabilities
- Reduced log volume through heartbeat filtering
- Consistent logging format across your application
Here’s a consolidated list of common NUnit Assert
statements, categorized by their purpose. This should cover most of the common scenarios:
Basic Assertions
-
Equality:
Assert.That(actual, Is.EqualTo(expected));
Assert.That(actual, Is.Not.EqualTo(expected));
-
Boolean Conditions:
Assert.That(condition, Is.True);
Assert.That(condition, Is.False);
-
Null Checks:
Assert.That(obj, Is.Null);
Assert.That(obj, Is.Not.Null);
String Assertions
-
Contains:
Assert.That(actualString, Does.Contain(substring));
-
Starts With / Ends With:
Assert.That(actualString, Does.StartWith(prefix));
Assert.That(actualString, Does.EndWith(suffix));
-
Empty or Not Empty:
Assert.That(actualString, Is.Empty);
Assert.That(actualString, Is.Not.Empty);
-
Matches Regex:
Assert.That(actualString, Does.Match(regexPattern));
Collection Assertions
-
Contains Item:
Assert.That(collection, Does.Contain(item));
-
Has Specific Count:
Assert.That(collection, Has.Count.EqualTo(expectedCount));
-
Empty or Not Empty:
Assert.That(collection, Is.Empty);
Assert.That(collection, Is.Not.Empty);
-
Unique Items:
Assert.That(collection, Is.Unique);
Numeric Assertions
-
Greater Than / Less Than:
Assert.That(actual, Is.GreaterThan(expected));
Assert.That(actual, Is.LessThan(expected));
-
Greater Than or Equal / Less Than or Equal:
Assert.That(actual, Is.GreaterThanOrEqualTo(expected));
Assert.That(actual, Is.LessThanOrEqualTo(expected));
-
In Range:
Assert.That(actual, Is.InRange(lower, upper));
Type Assertions
-
Instance of Type:
Assert.That(obj, Is.TypeOf<ExpectedType>());
Assert.That(obj, Is.InstanceOf<ExpectedType>());
-
Assignable From:
Assert.That(obj, Is.AssignableTo<ExpectedType>());
Exception Assertions
-
Throws Exception:
Assert.Throws<ExpectedExceptionType>(() => { methodCall(); });
-
Throws Specific Exception with Condition:
var ex = Assert.Throws<ExpectedExceptionType>(() => { methodCall(); });
Assert.That(ex.Message, Does.Contain("expected message"));
Miscellaneous
-
Same Instance:
Assert.That(actual, Is.SameAs(expected));
Assert.That(actual, Is.Not.SameAs(expected));
-
Applies a Condition:
Assert.That(collection, Has.Some.Matches<ExpectedType>(item => item.Condition));
-
Delayed Constraints (Asynchronous):
Assert.That(() => condition, Is.True.After(500).PollEvery(50));
-
Group related assertions together to improve readability and reporting:
Assert.Multiple(() =>
{
Assert.That(okResult, Is.Not.Null, "okResult should not be null");
Assert.That(okResult.Value, Is.TypeOf<string>(), "Value should be of type string");
});