Analyzers
DevBitsLab.Feeds includes Roslyn analyzers that help you follow best practices and catch common mistakes at compile time.
Installation
The analyzers are automatically included when you reference the DevBitsLab.Feeds NuGet package. No additional installation is required.
Version-Aware Behavior
The analyzers automatically adapt to your C# language version:
| C# Version | Behavior |
|---|---|
| C# 10-12 | FEED001 always required; FEED002/FEED004 not shown |
| C# 13+ | Full analyzer support including partial property suggestions |
Note: On C# versions before 13, partial properties are not available, so the analyzers won't suggest using them.
Analyzer Summary
| ID | Severity | Description | C# Version |
|---|---|---|---|
| FEED001 | ⚠️ Warning | Missing InitializeBindableFeeds() call |
All |
| FEED002 | ℹ️ Info | Unnecessary InitializeBindableFeeds() call |
13+ |
| FEED003 | ⚠️ Warning | Feed property should be readonly | All |
| FEED004 | ℹ️ Info | Consider using partial property | 13+ |
| FEED005 | ⚠️ Warning | Missing [BindableFeed] attribute |
All |
| FEED006 | ℹ️ Info | Debounce without UI context | All |
| FEED007 | ℹ️ Info | Identity Select is unnecessary | All |
| FEED008 | ⚠️ Warning | Filter predicate is constant | All |
FEED001
InitializeBindableFeeds() call required
| Property | Value |
|---|---|
| Severity | ⚠️ Warning |
| Category | Usage |
| C# Version | All |
When using [BindableFeed] on non-partial properties, you must call InitializeBindableFeeds() at the end of the constructor.
Note: On C# < 13, this is always required since partial properties aren't available.
Example
// ❌ Violation
public partial class MyViewModel : ObservableObject
{
[BindableFeed]
public Feed<Person> Person { get; }
public MyViewModel()
{
Person = Feed<Person>.FromValue(new Person());
// Missing InitializeBindableFeeds() call
}
}
// ✅ Fixed
public MyViewModel()
{
Person = Feed<Person>.FromValue(new Person());
InitializeBindableFeeds();
}
FEED002
InitializeBindableFeeds() call is unnecessary
| Property | Value |
|---|---|
| Severity | ℹ️ Info |
| Category | Usage |
| C# Version | 13+ only |
When all [BindableFeed] properties are partial, the generator handles initialization automatically.
Note: This diagnostic only appears when using C# 13 or later.
Example
// ℹ️ Suggestion
public partial class MyViewModel : ObservableObject
{
[BindableFeed]
public partial Feed<Person> Person { get; set; }
public MyViewModel()
{
Person = Feed<Person>.FromValue(new Person());
InitializeBindableFeeds(); // Unnecessary
}
}
FEED003
Feed property should be readonly
| Property | Value |
|---|---|
| Severity | ⚠️ Warning |
| Category | Design |
| C# Version | All |
Feed properties should not have public setters.
Example
// ❌ Violation
[BindableFeed]
public Feed<Person> Person { get; set; }
// ✅ Fixed options
public Feed<Person> Person { get; }
public Feed<Person> Person { get; private set; }
public Feed<Person> Person { get; init; }
FEED004
Consider using partial property
| Property | Value |
|---|---|
| Severity | ℹ️ Info |
| Category | Usage |
| C# Version | 13+ only |
Partial properties with [BindableFeed] are automatically initialized.
Note: This diagnostic only appears when using C# 13 or later.
Example
// ℹ️ Suggestion: Convert to partial
[BindableFeed]
public Feed<Person> Person { get; }
// ✅ Better: Partial property
[BindableFeed]
public partial Feed<Person> Person { get; set; }
FEED005
Missing [BindableFeed] attribute
| Property | Value |
|---|---|
| Severity | ⚠️ Warning |
| Category | Usage |
| C# Version | All |
Feed properties in ObservableObject should have [BindableFeed].
Example
// ❌ Violation
public partial class MyViewModel : ObservableObject
{
public Feed<Person> Person { get; }
}
// ✅ Fixed
public partial class MyViewModel : ObservableObject
{
[BindableFeed]
public Feed<Person> Person { get; }
}
FEED006
Debounce may not marshal to UI thread
| Property | Value |
|---|---|
| Severity | ℹ️ Info |
| Category | Usage |
When Debounce is called with useCallingContext: false, PropertyChanged events will fire on a background thread.
Example
// ℹ️ May cause UI binding issues
var debouncedFeed = sourceFeed.Debounce(
TimeSpan.FromMilliseconds(300),
useCallingContext: false);
// ✅ Default behavior marshals to UI thread
var debouncedFeed = sourceFeed.Debounce(TimeSpan.FromMilliseconds(300));
FEED007
Identity Select is unnecessary
| Property | Value |
|---|---|
| Severity | ℹ️ Info |
| Category | Performance |
A Select operation that returns its input unchanged adds overhead without transforming the data.
Example
// ❌ Unnecessary
var feed = sourceFeed.Select(x => x);
// ✅ Just use the source directly
var feed = sourceFeed;
FEED008
Filter predicate is constant
| Property | Value |
|---|---|
| Severity | ⚠️ Warning |
| Category | Correctness |
A Filter predicate that always returns true or false is usually a mistake.
Example
// ❌ Always true - no filtering
var feed = sourceFeed.Filter(_ => true);
// ❌ Always false - blocks all values
var feed = sourceFeed.Filter(_ => false);
// ✅ Use a meaningful predicate
var feed = sourceFeed.Filter(x => x.IsActive);
Suppressing Diagnostics
If you need to suppress a diagnostic:
Using Pragma
#pragma warning disable FEED003
public Feed<Person> Person { get; set; }
#pragma warning restore FEED003
Using SuppressMessage
[SuppressMessage("Design", "FEED003")]
public Feed<Person> Person { get; set; }
Using .editorconfig
[*.cs]
dotnet_diagnostic.FEED003.severity = none