@@ -178,8 +178,9 @@ namespace {{classInfo.Namespace}}
178178 #nullable disable
179179 #pragma warning disable
180180
181- using System.Linq ;
181+ using System;
182182 using System.Collections.Immutable;
183+ using System.Linq;
183184
184185 """ ;
185186
@@ -386,6 +387,12 @@ int nestingLevel
386387 return $ "{ sourceObjectName } .{ propertyName } ?.Select(x => x?.{ DeepCloneMethodName } ()).ToArray()";
387388 }
388389
390+ // Optimize for primitive and blittable types using span-based copy
391+ if ( IsPrimitiveOrBlittableType ( elementType ) )
392+ {
393+ return $ "{ sourceObjectName } .{ propertyName } != null ? { sourceObjectName } .{ propertyName } .AsSpan().ToArray() : null";
394+ }
395+
389396 if ( elementType . IsValueType || elementType . SpecialType == SpecialType . System_String )
390397 {
391398 return $ "{ sourceObjectName } .{ propertyName } != null ? ({ elementType . ToDisplayString ( SymbolDisplayFormat . FullyQualifiedFormat ) } []){ sourceObjectName } .{ propertyName } .Clone() : null";
@@ -575,6 +582,42 @@ private static bool IsCollectionType(INamedTypeSymbol type)
575582 ) ;
576583 }
577584
585+ private static bool IsPrimitiveOrBlittableType ( ITypeSymbol type )
586+ {
587+ // Check for primitive types that can be efficiently copied with memory operations
588+ switch ( type . SpecialType )
589+ {
590+ case SpecialType . System_Boolean :
591+ case SpecialType . System_Char :
592+ case SpecialType . System_SByte :
593+ case SpecialType . System_Byte :
594+ case SpecialType . System_Int16 :
595+ case SpecialType . System_UInt16 :
596+ case SpecialType . System_Int32 :
597+ case SpecialType . System_UInt32 :
598+ case SpecialType . System_Int64 :
599+ case SpecialType . System_UInt64 :
600+ case SpecialType . System_Single :
601+ case SpecialType . System_Double :
602+ case SpecialType . System_Decimal :
603+ case SpecialType . System_IntPtr :
604+ case SpecialType . System_UIntPtr :
605+ return true ;
606+ default :
607+ break ;
608+ }
609+
610+ // Check for other common blittable types
611+ var fullName = type . ToDisplayString ( SymbolDisplayFormat . FullyQualifiedFormat ) ;
612+ return fullName is "global::System.Guid"
613+ or "global::System.DateTime"
614+ or "global::System.TimeSpan"
615+ or "global::System.DateTimeOffset" ;
616+
617+ // Note: For custom structs, we would need to recursively check all fields
618+ // For now, we're conservative and only optimize known types
619+ }
620+
578621 private static string GenerateArrayDeepClone (
579622 IPropertySymbol property ,
580623 IArrayTypeSymbol arrayType
@@ -606,7 +649,23 @@ IArrayTypeSymbol arrayType
606649 }
607650 }
608651
609- if ( elementType . IsValueType || elementType . SpecialType == SpecialType . System_String )
652+ // Optimize for primitive and blittable types using span-based copy
653+ if ( IsPrimitiveOrBlittableType ( elementType ) )
654+ {
655+ // Use span-based copy for better performance - this uses memory copy under the hood
656+ return $ "this.{ propertyName } != null ? this.{ propertyName } .AsSpan().ToArray() : null";
657+ }
658+
659+ // For string arrays, use Array.Clone (strings are immutable so shallow copy is safe)
660+ if ( elementType . SpecialType == SpecialType . System_String )
661+ {
662+ var arrayTypeName =
663+ $ "{ elementType . ToDisplayString ( SymbolDisplayFormat . FullyQualifiedFormat ) } []";
664+ return $ "this.{ propertyName } != null ? ({ arrayTypeName } )this.{ propertyName } .Clone() : null";
665+ }
666+
667+ // For other value types (structs that may contain references), use Array.Clone
668+ if ( elementType . IsValueType )
610669 {
611670 var arrayTypeName =
612671 $ "{ elementType . ToDisplayString ( SymbolDisplayFormat . FullyQualifiedFormat ) } []";
@@ -697,6 +756,7 @@ INamedTypeSymbol dictionaryType
697756 {
698757 return $ "this.{ propertyName } != null ? new System.Collections.ObjectModel.ReadOnlyDictionary<{ keyType . ToDisplayString ( SymbolDisplayFormat . FullyQualifiedFormat ) } , { valueType . ToDisplayString ( SymbolDisplayFormat . FullyQualifiedFormat ) } >(this.{ propertyName } .ToDictionary(kvp => kvp.Key, kvp => { valueCloneExpression } )) : null";
699758 }
759+ // ReadOnlyDictionary wraps a mutable dictionary, so we need to clone it even for value types
700760 return $ "this.{ propertyName } != null ? new System.Collections.ObjectModel.ReadOnlyDictionary<{ keyType . ToDisplayString ( SymbolDisplayFormat . FullyQualifiedFormat ) } , { valueType . ToDisplayString ( SymbolDisplayFormat . FullyQualifiedFormat ) } >(new System.Collections.Generic.Dictionary<{ keyType . ToDisplayString ( SymbolDisplayFormat . FullyQualifiedFormat ) } , { valueType . ToDisplayString ( SymbolDisplayFormat . FullyQualifiedFormat ) } >(this.{ propertyName } )) : null";
701761 }
702762
@@ -848,6 +908,7 @@ bool isCloneable
848908 {
849909 return $ "this.{ propertyName } != null ? new System.Collections.ObjectModel.ReadOnlyCollection<{ elementType . ToDisplayString ( SymbolDisplayFormat . FullyQualifiedFormat ) } >(this.{ propertyName } .Select(x => x?.{ DeepCloneMethodName } ()).ToList()) : null";
850910 }
911+ // ReadOnlyCollection wraps a mutable list, so we need to clone it even for value types
851912 return $ "this.{ propertyName } != null ? new System.Collections.ObjectModel.ReadOnlyCollection<{ elementType . ToDisplayString ( SymbolDisplayFormat . FullyQualifiedFormat ) } >(this.{ propertyName } .ToList()) : null";
852913 }
853914
@@ -861,6 +922,12 @@ bool isCloneable
861922 {
862923 return $ "this.{ propertyName } ?.Select(x => x?.{ DeepCloneMethodName } ()).ToImmutableList()";
863924 }
925+ // For value types and strings in immutable collections, we can safely reuse the same collection
926+ // since both the collection and elements are immutable
927+ if ( elementType . IsValueType || elementType . SpecialType == SpecialType . System_String )
928+ {
929+ return $ "this.{ propertyName } ";
930+ }
864931 return $ "this.{ propertyName } ?.ToImmutableList()";
865932 }
866933
@@ -874,6 +941,12 @@ bool isCloneable
874941 {
875942 return $ "this.{ propertyName } .IsDefault ? default : this.{ propertyName } .Select(x => x?.{ DeepCloneMethodName } ()).ToImmutableArray()";
876943 }
944+ // For value types and strings in immutable arrays, we can safely reuse the same array
945+ // since both the array and elements are immutable
946+ if ( elementType . IsValueType || elementType . SpecialType == SpecialType . System_String )
947+ {
948+ return $ "this.{ propertyName } ";
949+ }
877950 return $ "this.{ propertyName } .IsDefault ? default : this.{ propertyName } .ToImmutableArray()";
878951 }
879952
@@ -887,6 +960,12 @@ bool isCloneable
887960 {
888961 return $ "this.{ propertyName } ?.Select(x => x?.{ DeepCloneMethodName } ()).ToImmutableHashSet()";
889962 }
963+ // For value types and strings in immutable collections, we can safely reuse the same collection
964+ // since both the collection and elements are immutable
965+ if ( elementType . IsValueType || elementType . SpecialType == SpecialType . System_String )
966+ {
967+ return $ "this.{ propertyName } ";
968+ }
890969 return $ "this.{ propertyName } ?.ToImmutableHashSet()";
891970 }
892971
@@ -900,6 +979,11 @@ bool isCloneable
900979 {
901980 return $ "this.{ propertyName } == null ? System.Collections.Immutable.ImmutableQueue<{ elementType . ToDisplayString ( SymbolDisplayFormat . FullyQualifiedFormat ) } >.Empty : System.Collections.Immutable.ImmutableQueue.CreateRange(this.{ propertyName } .Select(x => x?.{ DeepCloneMethodName } ()))";
902981 }
982+ // For value types and strings in immutable collections, we can safely reuse the same collection
983+ if ( elementType . IsValueType || elementType . SpecialType == SpecialType . System_String )
984+ {
985+ return $ "this.{ propertyName } ";
986+ }
903987 return $ "this.{ propertyName } == null ? System.Collections.Immutable.ImmutableQueue<{ elementType . ToDisplayString ( SymbolDisplayFormat . FullyQualifiedFormat ) } >.Empty : System.Collections.Immutable.ImmutableQueue.CreateRange(this.{ propertyName } )";
904988 }
905989
@@ -913,6 +997,11 @@ bool isCloneable
913997 {
914998 return $ "this.{ propertyName } == null ? System.Collections.Immutable.ImmutableStack<{ elementType . ToDisplayString ( SymbolDisplayFormat . FullyQualifiedFormat ) } >.Empty : System.Collections.Immutable.ImmutableStack.CreateRange(this.{ propertyName } .Select(x => x?.{ DeepCloneMethodName } ()))";
915999 }
1000+ // For value types and strings in immutable collections, we can safely reuse the same collection
1001+ if ( elementType . IsValueType || elementType . SpecialType == SpecialType . System_String )
1002+ {
1003+ return $ "this.{ propertyName } ";
1004+ }
9161005 return $ "this.{ propertyName } == null ? System.Collections.Immutable.ImmutableStack<{ elementType . ToDisplayString ( SymbolDisplayFormat . FullyQualifiedFormat ) } >.Empty : System.Collections.Immutable.ImmutableStack.CreateRange(this.{ propertyName } )";
9171006 }
9181007
@@ -957,6 +1046,8 @@ elementType is INamedTypeSymbol namedElementTypeRef
9571046 }
9581047 }
9591048
1049+ // For value types including primitives and strings, the List constructor is already optimized
1050+ // and will use Array.Copy internally which is very efficient
9601051 return $ "this.{ propertyName } != null ? new System.Collections.Generic.List<{ elementType . ToDisplayString ( SymbolDisplayFormat . FullyQualifiedFormat ) } >(this.{ propertyName } ) : null";
9611052 }
9621053
0 commit comments