Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions .github/instructions/codestyle.instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -566,3 +566,16 @@ Within a class, struct, or interface, elements should be positioned in the follo
11. Records
12. Structs
13. Classes

### Try Pattern

When a method name begins with `Try` and returns a variable, ensure it adheres to the Try Pattern.

Here Key Components of the Try Pattern:
1. Method Name: Begins with Try (e.g., TryParse, TryGetValue).
2. Return Type: bool (indicating success or failure).
3. Out Parameter: An out parameter to return the result if successful.
4. Null Analysis Attribute: [NotNullWhen(true)] (or [MaybeNullWhen(false)]) informs the compiler that if the method returns true, the out parameter is guaranteed to be non-null.

This pattern allows for high-performance retrieval or parsing without throwing exceptions for expected failures. It also allows cleaner call sites by eliminating the need for null-checking the output variable within the if block, as seen in Dictionary<TKey, TValue>.TryGetValue.
```
Original file line number Diff line number Diff line change
Expand Up @@ -598,8 +598,8 @@ public partial class {{defaultTestClassName}}
/// <summary>
/// BindableProperty for the <see cref = "Text"/> property.
/// </summary>
public static readonly global::Microsoft.Maui.Controls.BindableProperty TextProperty = global::Microsoft.Maui.Controls.BindableProperty.Create("Text", typeof(string), typeof({{defaultTestNamespace}}.{{defaultTestClassName}}), null, (Microsoft.Maui.Controls.BindingMode)0, null, null, null, null, __{{defaultTestClassName}}BindablePropertyInitHelpers.CreateDefaultText);
public partial string Text { get => __{{defaultTestClassName}}BindablePropertyInitHelpers.IsInitializingText ? field : (string)GetValue(TextProperty); set => SetValue(TextProperty, value); }
public static readonly global::Microsoft.Maui.Controls.BindableProperty TextProperty = global::Microsoft.Maui.Controls.BindableProperty.Create("Text", typeof(string), typeof({{defaultTestNamespace}}.{{defaultTestClassName}}), "Initial Value", (Microsoft.Maui.Controls.BindingMode)0, null, null, null, null, null);
public partial string Text { get => false ? field : (string)GetValue(TextProperty); set => SetValue(TextProperty, value); }

/// <summary>
/// BindableProperty for the <see cref = "CustomDuration"/> property.
Expand All @@ -610,20 +610,11 @@ public partial class {{defaultTestClassName}}

file static class __{{defaultTestClassName}}BindablePropertyInitHelpers
{
public static volatile bool IsInitializingText = false;
public static object CreateDefaultText(global::Microsoft.Maui.Controls.BindableObject bindable)
{
IsInitializingText = true;
var defaultValue = ((TestView)bindable).Text;
IsInitializingText = false;
return defaultValue;
}

public static volatile bool IsInitializingCustomDuration = false;
public static object CreateDefaultCustomDuration(global::Microsoft.Maui.Controls.BindableObject bindable)
{
IsInitializingCustomDuration = true;
var defaultValue = ((TestView)bindable).CustomDuration;
var defaultValue = (({{defaultTestClassName}})bindable).CustomDuration;
IsInitializingCustomDuration = false;
return defaultValue;
}
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -162,21 +162,8 @@ public partial class {{defaultTestClassName}}<T, U>
/// <summary>
/// BindableProperty for the <see cref = "Value"/> property.
/// </summary>
public static readonly global::Microsoft.Maui.Controls.BindableProperty ValueProperty = global::Microsoft.Maui.Controls.BindableProperty.Create("Value", typeof(T), typeof({{defaultTestNamespace}}.{{defaultTestClassName}}<T, U>), null, (Microsoft.Maui.Controls.BindingMode)0, null, null, null, null, __{{defaultTestClassName}}BindablePropertyInitHelpers.CreateDefaultValue);
public partial T? Value { get => __{{defaultTestClassName}}BindablePropertyInitHelpers.IsInitializingValue ? field : (T? )GetValue(ValueProperty); set => SetValue(ValueProperty, value); }

[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
private static class __{{defaultTestClassName}}BindablePropertyInitHelpers
{
public static volatile bool IsInitializingValue = false;
public static object CreateDefaultValue(global::Microsoft.Maui.Controls.BindableObject bindable)
{
IsInitializingValue = true;
var defaultValue = (({{defaultTestClassName}}<T, U>)bindable).Value;
IsInitializingValue = false;
return defaultValue;
}
}
public static readonly global::Microsoft.Maui.Controls.BindableProperty ValueProperty = global::Microsoft.Maui.Controls.BindableProperty.Create("Value", typeof(T), typeof({{defaultTestNamespace}}.{{defaultTestClassName}}<T, U>), default(T), (Microsoft.Maui.Controls.BindingMode)0, null, null, null, null, null);
public partial T? Value { get => false ? field : (T? )GetValue(ValueProperty); set => SetValue(ValueProperty, value); }
}
""";

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ public void BindablePropertyName_ReturnsCorrectPropertyName()
true, // IsReadOnlyBindableProperty
string.Empty, // SetterAccessibility
false,
"null"
"null",
null
);

// Act
Expand Down Expand Up @@ -74,7 +75,8 @@ public void BindablePropertyModel_WithAllParameters_StoresCorrectValues()
true, // IsReadOnlyBindableProperty
string.Empty, // SetterAccessibility
hasInitializer,
propertyAccessibility
propertyAccessibility,
null
);

// Assert
Expand Down Expand Up @@ -135,7 +137,8 @@ public void SemanticValues_WithClassInfoAndProperties_StoresCorrectValues()
true, // IsReadOnlyBindableProperty
string.Empty, // SetterAccessibilityText
false,
"public"
"public",
null
);

var bindableProperties = new[] { bindableProperty }.ToImmutableArray();
Expand Down Expand Up @@ -194,7 +197,8 @@ public class TestClass { public string TestProperty { get; set; } = "Initial Val
true,
string.Empty,
hasInitializer,
"public"
"public",
null
);

// Act
Expand Down Expand Up @@ -234,7 +238,8 @@ public class TestClass { public string TestProperty { get; set; } = "Initial Val
true,
string.Empty,
hasInitializer,
"public"
"public",
null
);

// Act
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,9 @@ static void GenerateReadOnlyBindableProperty(StringBuilder sb, in BindableProper
.Append(GetFormattedReturnType(nonNullableReturnType))
.Append("), typeof(")
.Append(info.DeclaringType)
.Append("), null, ")
.Append("), ")
.Append(info.ResolvedInitializerExpression ?? "null")
.Append(", ")
.Append(info.DefaultBindingMode)
.Append(", ")
.Append(info.ValidateValueMethodName)
Expand Down Expand Up @@ -304,7 +306,9 @@ static void GenerateBindableProperty(StringBuilder sb, in BindablePropertyModel
.Append(GetFormattedReturnType(nonNullableReturnType))
.Append("), typeof(")
.Append(info.DeclaringType)
.Append("), null, ")
.Append("), ")
.Append(info.ResolvedInitializerExpression ?? "null")
.Append(", ")
.Append(info.DefaultBindingMode)
.Append(", ")
.Append(info.ValidateValueMethodName)
Expand Down Expand Up @@ -408,8 +412,15 @@ static BindablePropertySemanticValues SemanticTransform(GeneratorAttributeSyntax

var propertyAccessibility = GetPropertyAccessibility(propertySymbol);

// Resolve initializer expression to a fully qualified string for use as defaultValue
string? resolvedInitializer = null;
if (hasInitializer && propertyDeclarationSyntax.Initializer is not null)
{
InitializerExpressionResolver.TryResolve(propertyDeclarationSyntax.Initializer.Value, semanticModel, out resolvedInitializer);
}

var attributeData = context.Attributes[0];
bindablePropertyModels[0] = CreateBindablePropertyModel(attributeData, propertySymbol.ContainingType, propertySymbol.Name, returnType, doesContainNewKeyword, isReadOnlyBindableProperty, setterAccessibility, hasInitializer, propertyAccessibility);
bindablePropertyModels[0] = CreateBindablePropertyModel(attributeData, propertySymbol.ContainingType, propertySymbol.Name, returnType, doesContainNewKeyword, isReadOnlyBindableProperty, setterAccessibility, hasInitializer, propertyAccessibility, resolvedInitializer);

return new(propertyInfo, ImmutableArray.Create(bindablePropertyModels));
}
Expand Down Expand Up @@ -517,7 +528,7 @@ static string GetGenericTypeParameters(INamedTypeSymbol typeSymbol)
return sb.ToString();
}

static BindablePropertyModel CreateBindablePropertyModel(in AttributeData attributeData, in INamedTypeSymbol declaringType, in string propertyName, in ITypeSymbol returnType, in bool doesContainNewKeyword, in bool isReadOnly, in string? setterAccessibility, in bool hasInitializer, in string? propertyAccessibility)
static BindablePropertyModel CreateBindablePropertyModel(in AttributeData attributeData, in INamedTypeSymbol declaringType, in string propertyName, in ITypeSymbol returnType, in bool doesContainNewKeyword, in bool isReadOnly, in string? setterAccessibility, in bool hasInitializer, in string? propertyAccessibility, in string? resolvedInitializerExpression)
{
if (attributeData.AttributeClass is null)
{
Expand All @@ -532,7 +543,10 @@ static BindablePropertyModel CreateBindablePropertyModel(in AttributeData attrib
var validateValueMethodName = attributeData.GetNamedMethodGroupArgumentsAttributeValueByNameAsString(nameof(BindablePropertyModel.ValidateValueMethodName));
var newKeywordText = doesContainNewKeyword ? "new " : string.Empty;

return new BindablePropertyModel(propertyName, returnType, declaringType, defaultBindingMode, validateValueMethodName, propertyChangedMethodName, propertyChangingMethodName, coerceValueMethodName, defaultValueCreatorMethodName, newKeywordText, isReadOnly, setterAccessibility, hasInitializer, propertyAccessibility);
// Only use the resolved initializer when there's no explicit DefaultValueCreatorMethodName
var effectiveResolvedInitializer = defaultValueCreatorMethodName is "null" ? resolvedInitializerExpression : null;

return new BindablePropertyModel(propertyName, returnType, declaringType, defaultBindingMode, validateValueMethodName, propertyChangedMethodName, propertyChangingMethodName, coerceValueMethodName, defaultValueCreatorMethodName, newKeywordText, isReadOnly, setterAccessibility, hasInitializer, propertyAccessibility, effectiveResolvedInitializer);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
Expand Down
Loading
Loading