Skip to content

Commit 101336e

Browse files
Added the ability to use static properties with the Property attribute
1 parent 98592bf commit 101336e

File tree

1 file changed

+195
-23
lines changed

1 file changed

+195
-23
lines changed

src/ThunderDesign.Net-PCL.SourceGenerators/UnifiedPropertyGenerator.cs

Lines changed: 195 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,8 @@ private static void GenerateUnifiedPropertyClass(
165165
classSymbol,
166166
propertyChangedEventType,
167167
stringTypeSymbol,
168-
voidTypeSymbol);
168+
voidTypeSymbol,
169+
propertyFields); // Pass propertyFields to check for static properties
169170

170171
// Generate properties
171172
GenerateBindableProperties(source, bindableFields, classSymbol, compilation);
@@ -193,6 +194,14 @@ private static bool ValidateFields(
193194
// Check bindable fields
194195
foreach (var info in bindableFields)
195196
{
197+
// Add this validation for static fields with BindableProperty
198+
if (info.FieldSymbol.IsStatic)
199+
{
200+
PropertyGeneratorHelpers.ReportDiagnostic(context, info.FieldDeclaration.GetLocation(),
201+
$"Static fields cannot use [BindableProperty]. Use [Property] instead for field '{info.FieldSymbol.Name}'.")
202+
; return false;
203+
}
204+
196205
if (!PropertyGeneratorHelpers.IsPartial(classSymbol))
197206
{
198207
PropertyGeneratorHelpers.ReportDiagnostic(context, info.FieldDeclaration.GetLocation(),
@@ -281,8 +290,12 @@ private static void GenerateInfrastructureMembers(
281290
INamedTypeSymbol classSymbol,
282291
INamedTypeSymbol propertyChangedEventType,
283292
ITypeSymbol stringTypeSymbol,
284-
ITypeSymbol voidTypeSymbol)
293+
ITypeSymbol voidTypeSymbol,
294+
List<PropertyFieldInfo> propertyFields)
285295
{
296+
// Check if we have any static property fields
297+
bool hasStaticProperties = propertyFields.Any(p => p.FieldSymbol.IsStatic);
298+
286299
// Add event if needed
287300
if (bindableFields.Count > 0 && !implementsINotify &&
288301
!PropertyGeneratorHelpers.EventExists(classSymbol, "PropertyChanged", propertyChangedEventType))
@@ -296,6 +309,12 @@ private static void GenerateInfrastructureMembers(
296309
source.AppendLine(" protected readonly object _Locker = new object();");
297310
}
298311

312+
// Add static _StaticLocker if we have static properties
313+
if (hasStaticProperties && !PropertyGeneratorHelpers.FieldExists(classSymbol, "_StaticLocker"))
314+
{
315+
source.AppendLine(" static readonly object _StaticLocker = new object();");
316+
}
317+
299318
// Add OnPropertyChanged if needed
300319
if (bindableFields.Count > 0 && !implementsIBindable && !PropertyGeneratorHelpers.MethodExists(
301320
classSymbol,
@@ -312,6 +331,12 @@ private static void GenerateInfrastructureMembers(
312331
this.NotifyPropertyChanged(PropertyChanged, propertyName);
313332
}}");
314333
}
334+
335+
// Add static property helper methods if we have static properties
336+
if (hasStaticProperties)
337+
{
338+
GenerateStaticPropertyHelpers(source, classSymbol);
339+
}
315340
}
316341

317342
private static void GenerateBindableProperties(
@@ -501,6 +526,7 @@ private static void GenerateRegularProperties(
501526

502527
var fieldSymbol = info.FieldSymbol;
503528
var fieldName = fieldSymbol.Name;
529+
var isStatic = fieldSymbol.IsStatic;
504530

505531
// Use NullableFlowState-aware display string to properly handle nullable types
506532
var typeName = GetNullableAwareTypeName(fieldSymbol.Type, compilation);
@@ -523,36 +549,115 @@ private static void GenerateRegularProperties(
523549
string propertyAccessRaw = readOnly ? getterValue : GetWidestAccessibility(getterValue, setterValue);
524550
string propertyAccessibilityStr = ToPropertyAccessibilityString(propertyAccessRaw);
525551

526-
var lockerArg = threadSafe ? "_Locker" : "null";
552+
var lockerArg = isStatic ? (threadSafe ? "_StaticLocker" : "null") : (threadSafe ? "_Locker" : "null");
527553

528-
if (readOnly)
554+
if (isStatic)
529555
{
530-
GenerateReadOnlyProperty(
531-
source,
532-
propertyAccessibilityStr,
533-
typeName,
534-
propertyName,
535-
getterValue,
536-
propertyAccessRaw,
537-
fieldName,
538-
lockerArg);
556+
if (readOnly)
557+
{
558+
GenerateReadOnlyStaticProperty(
559+
source,
560+
propertyAccessibilityStr,
561+
typeName,
562+
propertyName,
563+
getterValue,
564+
propertyAccessRaw,
565+
fieldName,
566+
lockerArg);
567+
}
568+
else
569+
{
570+
GenerateReadWriteStaticProperty(
571+
source,
572+
propertyAccessibilityStr,
573+
typeName,
574+
propertyName,
575+
getterValue,
576+
propertyAccessRaw,
577+
fieldName,
578+
lockerArg,
579+
setterValue);
580+
}
539581
}
540582
else
541583
{
542-
GenerateReadWriteProperty(
543-
source,
544-
propertyAccessibilityStr,
545-
typeName,
546-
propertyName,
547-
getterValue,
548-
propertyAccessRaw,
549-
fieldName,
550-
lockerArg,
551-
setterValue);
584+
if (readOnly)
585+
{
586+
GenerateReadOnlyProperty(
587+
source,
588+
propertyAccessibilityStr,
589+
typeName,
590+
propertyName,
591+
getterValue,
592+
propertyAccessRaw,
593+
fieldName,
594+
lockerArg);
595+
}
596+
else
597+
{
598+
GenerateReadWriteProperty(
599+
source,
600+
propertyAccessibilityStr,
601+
typeName,
602+
propertyName,
603+
getterValue,
604+
propertyAccessRaw,
605+
fieldName,
606+
lockerArg,
607+
setterValue);
608+
}
552609
}
553610
}
554611
}
555612

613+
private static void GenerateReadOnlyStaticProperty(
614+
StringBuilder source,
615+
string propertyAccessibilityStr,
616+
string typeName,
617+
string propertyName,
618+
string getterValue,
619+
string propertyAccessRaw,
620+
string fieldName,
621+
string lockerArg)
622+
{
623+
string getterModifier = getterValue.Equals(propertyAccessRaw, StringComparison.OrdinalIgnoreCase)
624+
? ""
625+
: ToPropertyAccessibilityString(getterValue);
626+
627+
source.AppendLine($@"
628+
{propertyAccessibilityStr}static {typeName} {propertyName}
629+
{{
630+
{getterModifier}get {{ return GetStaticProperty(ref {fieldName}, {lockerArg}); }}
631+
}}");
632+
}
633+
634+
private static void GenerateReadWriteStaticProperty(
635+
StringBuilder source,
636+
string propertyAccessibilityStr,
637+
string typeName,
638+
string propertyName,
639+
string getterValue,
640+
string propertyAccessRaw,
641+
string fieldName,
642+
string lockerArg,
643+
string setterValue)
644+
{
645+
string getterModifier = getterValue.Equals(propertyAccessRaw, StringComparison.OrdinalIgnoreCase)
646+
? ""
647+
: ToPropertyAccessibilityString(getterValue);
648+
649+
string setterModifier = setterValue.Equals(propertyAccessRaw, StringComparison.OrdinalIgnoreCase)
650+
? ""
651+
: ToPropertyAccessibilityString(setterValue);
652+
653+
source.AppendLine($@"
654+
{propertyAccessibilityStr}static {typeName} {propertyName}
655+
{{
656+
{getterModifier}get {{ return GetStaticProperty(ref {fieldName}, {lockerArg}); }}
657+
{setterModifier}set {{ SetStaticProperty(ref {fieldName}, value, {lockerArg}); }}
658+
}}");
659+
}
660+
556661
private static void GenerateReadOnlyProperty(
557662
StringBuilder source,
558663
string propertyAccessibilityStr,
@@ -638,6 +743,73 @@ private static string GetWidestAccessibility(string getter, string setter)
638743
return getterRank >= setterRank ? getter : setter;
639744
}
640745

746+
private static void GenerateStaticPropertyHelpers(StringBuilder source, INamedTypeSymbol classSymbol)
747+
{
748+
// Check if GetStaticProperty method already exists
749+
var genericMethodExists = classSymbol.GetMembers()
750+
.OfType<IMethodSymbol>()
751+
.Any(m => m.Name == "GetStaticProperty" && m.IsStatic && m.IsGenericMethod);
752+
753+
if (!genericMethodExists)
754+
{
755+
source.AppendLine(@"
756+
public static T GetStaticProperty<T>(
757+
ref T backingStore,
758+
object? lockObj = null)
759+
{
760+
bool lockWasTaken = false;
761+
try
762+
{
763+
if (lockObj != null)
764+
System.Threading.Monitor.Enter(lockObj, ref lockWasTaken);
765+
return backingStore;
766+
}
767+
finally
768+
{
769+
if (lockWasTaken)
770+
System.Threading.Monitor.Exit(lockObj!);
771+
}
772+
}");
773+
}
774+
775+
// Check if SetStaticProperty method already exists
776+
var setMethodExists = classSymbol.GetMembers()
777+
.OfType<IMethodSymbol>()
778+
.Any(m => m.Name == "SetStaticProperty" && m.IsStatic && m.IsGenericMethod);
779+
780+
if (!setMethodExists)
781+
{
782+
source.AppendLine(@"
783+
public static bool SetStaticProperty<T>(
784+
ref T backingStore,
785+
T value,
786+
object? lockObj = null,
787+
[System.Runtime.CompilerServices.CallerMemberName] string propertyName = """")
788+
{
789+
bool lockWasTaken = false;
790+
try
791+
{
792+
if (lockObj != null)
793+
System.Threading.Monitor.Enter(lockObj, ref lockWasTaken);
794+
if (System.Collections.Generic.EqualityComparer<T>.Default.Equals(backingStore, value))
795+
{
796+
return false;
797+
}
798+
else
799+
{
800+
backingStore = value;
801+
return true;
802+
}
803+
}
804+
finally
805+
{
806+
if (lockWasTaken)
807+
System.Threading.Monitor.Exit(lockObj!);
808+
}
809+
}");
810+
}
811+
}
812+
641813
private struct BindableFieldInfo
642814
{
643815
public IFieldSymbol FieldSymbol { get; set; }

0 commit comments

Comments
 (0)