Skip to content

Commit 10aeb7b

Browse files
CopiloteiriktsarpalisCopilot
authored
Support open generics in polymorphic serialization in System.Text.Json (#127318)
## Summary Adds support for applying `[JsonDerivedType(typeof(Derived<>))]` to a generic base type. The polymorphic resolver and source generator each unify the open derived type against the closed base and, when a single closed derived type can be constructed, register it for serialization. This is groundwork for the upcoming C# [closed hierarchies][closed] language feature, which allows generic closed base classes. Today, polymorphic serialization requires every derived type to be spelled out as a fully closed generic instantiation — workable for non-generic hierarchies, but impractical for generic ones where each combination of the base's type arguments yields a distinct closed instantiation that the author would otherwise have to enumerate by hand. Tracking: part of the work for #125449. [closed]: https://github.com/dotnet/csharplang/blob/main/proposals/closed-hierarchies.md ## Patterns now admitted Each example uses regular classes; for each pattern the derived attribute is declared once with an open generic, and the closed forms are resolved per serialized instantiation of the base. All of these compile (source generator) and round-trip (reflection): 1. **Matching arity / identity binding** — derived passes the base's type parameter through unchanged. ```csharp [JsonDerivedType(typeof(Derived<>), "d")] public class Base<T> { ... } public class Derived<T> : Base<T> { ... } // Base<int> → Derived<int> // Base<string> → Derived<string> ``` 2. **Reordered parameters** — derived rebinds the base's parameters in a different position. ```csharp [JsonDerivedType(typeof(Derived<,>), "d")] public class Base<T1, T2> { ... } public class Derived<U, V> : Base<V, U> { ... } // Base<int, string> → Derived<string, int> ``` 3. **Partial concretization in the derived's base spec** — derived fixes some of the base's parameters to concrete types and leaves others open. ```csharp [JsonDerivedType(typeof(Derived<>), "d")] public class Base<T1, T2> { ... } public class Derived<T> : Base<T, int> { ... } // Base<string, int> → Derived<string> // Base<bool, int> → Derived<bool> ``` 4. **Wrapped / nested type arguments** — derived's type parameter shows up inside a generic construction (or array) in the base spec. ```csharp [JsonDerivedType(typeof(Derived<>), "d")] public class Base<T> { ... } public class Derived<T> : Base<List<T>> { ... } // Base<List<int>> → Derived<int> [JsonDerivedType(typeof(ArrayDerived<>), "a")] public class ArrayDerived<T> : Base<T[]> { ... } // Base<int[]> → ArrayDerived<int> ``` 5. **Interface bases** — same unification logic applies when the base is a generic interface. ```csharp [JsonDerivedType(typeof(Derived<>), "d")] public interface IBase<T> { ... } public class Derived<T> : IBase<T> { ... } // IBase<int> → Derived<int> ``` 6. **Type parameters from enclosing types** — derived inherits part of its type arguments from an outer generic class. ```csharp [JsonDerivedType(typeof(Outer<>.Leaf<>), "leaf")] public class Base<T> { ... } public class Outer<T> { public class Leaf<U> : Base<(T, U)> { ... } } // Base<(int, string)> → Outer<int>.Leaf<string> ``` ## Patterns still not supported (loud failure) Each of these emits **SYSLIB1229** at source-generation time and throws `InvalidOperationException` at reflection-resolver `Configure()` time. The restrictions mirror the C# closed-hierarchies rules ("all of the derived's type parameters must be used in the base class specification"), with the additional requirement that the unification be unambiguous. 1. **Ground-position mismatch** — derived constrains a base parameter to a concrete type that the closed base doesn't agree with. ```csharp [JsonDerivedType(typeof(Derived<>), "d")] public class Base<T1, T2> { ... } public class Derived<T> : Base<T, int> { ... } // Base<int, string> → ✗ (Derived requires T2 == int, but base has T2 == string) ``` 2. **Wrapping not present on the closed base** — derived's base spec wraps the parameter in a generic that the closed base doesn't carry. ```csharp [JsonDerivedType(typeof(Derived<>), "d")] public class Base<T> { ... } public class Derived<T> : Base<List<T>> { ... } // Base<int> → ✗ (closed base T is `int`, not `List<...>`) // Base<List<int>> → ✓ Derived<int> (admitted by pattern #4) ``` 3. **Unbound derived type parameters** — derived declares more type parameters than the base substitution can pin down (the closed-hierarchies spec rejects this form directly). ```csharp [JsonDerivedType(typeof(Derived<,>), "d")] public class Base<T> { ... } public class Derived<T, U> : Base<T> { ... } // Base<int> → ✗ (U is unbound) ``` 4. **Constraint violation under the resolved substitution** — unification succeeds structurally but the closed derived type would violate a `where` constraint. ```csharp [JsonDerivedType(typeof(Derived<>), "d")] public class Base<T> { ... } public class Derived<T> : Base<T> where T : struct { ... } // Base<string> → ✗ (string does not satisfy `where T : struct`) ``` 5. **Ambiguous match** — derived implements two distinct constructions of the same generic base, so the closed base could correspond to either. ```csharp [JsonDerivedType(typeof(Derived<>), "d")] public interface IBase<T> { ... } public class Derived<T> : IBase<T>, IBase<int> { ... } // IBase<int> → ✗ (could be Derived<int> or Derived<T> for any T) ``` 6. **Arity / shape mismatch** — derived's open form has no constructed base in common with the closed base at all. ```csharp [JsonDerivedType(typeof(Derived<>), "d")] public class Base { ... } // non-generic public class Derived<T> : Base { ... } // Base → ✗ (closed base has no type arguments to bind T) ``` ## Diagnostic | ID | Severity | Message | | ---------- | -------- | --------------------------------------------------------------------------------------- | | SYSLIB1229 | Warning | The open generic derived type `'{0}'` could not be resolved against base `'{1}'`: `{2}` | SYSLIB1229 is `#pragma`-suppressible, in which case the offending derived entry is simply skipped from the generated metadata. ## Checklist - [x] New tests in `JsonSourceGeneratorDiagnosticsTests` and `PolymorphicTests.CustomTypeHierarchies` cover every supported and unsupported pattern listed above. - [x] Reflection resolver and source generator carry cross-referencing comments on their two `TryResolveOpenGenericDerivedType` implementations so the algorithms stay in sync. - [x] SYSLIB1229 added to `docs/project/list-of-diagnostics.md`. - [x] All reviewer feedback addressed. - [x] All review threads resolved. Co-authored-by: Eirik Tsarpalis <eirik.tsarpalis@gmail.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 849bb26 commit 10aeb7b

26 files changed

Lines changed: 3337 additions & 18 deletions

docs/project/list-of-diagnostics.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -274,7 +274,7 @@ The diagnostic id values reserved for .NET Libraries analyzer warnings are `SYSL
274274
| __`SYSLIB1226`__ | 'JsonIgnoreCondition.Always' is not valid on type-level 'JsonIgnoreAttribute' annotations. |
275275
| __`SYSLIB1227`__ | Union case types cannot be unambiguously classified by JSON value type. |
276276
| __`SYSLIB1228`__ | Union type shape is not a valid C# union. |
277-
| __`SYSLIB1229`__ | _`SYSLIB1220`-`SYSLIB1229` reserved for System.Text.Json.SourceGeneration._ |
277+
| __`SYSLIB1229`__ | Open generic derived type could not be resolved for the polymorphic base type. |
278278
| __`SYSLIB1230`__ | Deriving from a `GeneratedComInterface`-attributed interface defined in another assembly is not supported. |
279279
| __`SYSLIB1231`__ | _`SYSLIB1230`-`SYSLIB1239` reserved for Microsoft.Interop.ComInterfaceGenerator._ |
280280
| __`SYSLIB1232`__ | _`SYSLIB1230`-`SYSLIB1239` reserved for Microsoft.Interop.ComInterfaceGenerator._ |

src/libraries/System.Text.Json/gen/Helpers/RoslynExtensions.cs

Lines changed: 416 additions & 9 deletions
Large diffs are not rendered by default.

src/libraries/System.Text.Json/gen/JsonSourceGenerator.DiagnosticDescriptors.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,14 @@ internal static class DiagnosticDescriptors
147147
category: JsonConstants.SystemTextJsonSourceGenerationName,
148148
defaultSeverity: DiagnosticSeverity.Warning,
149149
isEnabledByDefault: true);
150+
151+
public static DiagnosticDescriptor OpenGenericDerivedTypeCouldNotBeResolved { get; } = DiagnosticDescriptorHelper.Create(
152+
id: "SYSLIB1229",
153+
title: new LocalizableResourceString(nameof(SR.OpenGenericDerivedTypeCouldNotBeResolvedTitle), SR.ResourceManager, typeof(FxResources.System.Text.Json.SourceGeneration.SR)),
154+
messageFormat: new LocalizableResourceString(nameof(SR.OpenGenericDerivedTypeCouldNotBeResolvedMessageFormat), SR.ResourceManager, typeof(FxResources.System.Text.Json.SourceGeneration.SR)),
155+
category: JsonConstants.SystemTextJsonSourceGenerationName,
156+
defaultSeverity: DiagnosticSeverity.Warning,
157+
isEnabledByDefault: true);
150158
}
151159
}
152160
}

src/libraries/System.Text.Json/gen/JsonSourceGenerator.Parser.cs

Lines changed: 220 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using System.Collections.Immutable;
66
using System.Diagnostics;
77
using System.Diagnostics.CodeAnalysis;
8+
using System.Globalization;
89
using System.Linq;
910
using System.Text.Json.Serialization;
1011
using System.Threading;
@@ -938,6 +939,20 @@ private void ProcessTypeCustomAttributes(
938939
{
939940
Debug.Assert(attributeData.ConstructorArguments.Length > 0);
940941
var derivedType = (ITypeSymbol)attributeData.ConstructorArguments[0].Value!;
942+
943+
if (derivedType is INamedTypeSymbol { IsUnboundGenericType: true } unboundDerived)
944+
{
945+
if (!TryResolveOpenGenericDerivedType(
946+
unboundDerived, typeToGenerate.Type,
947+
out INamedTypeSymbol? resolvedType, out string? failureReason))
948+
{
949+
ReportDiagnostic(DiagnosticDescriptors.OpenGenericDerivedTypeCouldNotBeResolved, attributeData.GetLocation(), derivedType.ToDisplayString(), typeToGenerate.Type.ToDisplayString(), failureReason);
950+
continue;
951+
}
952+
953+
derivedType = resolvedType!;
954+
}
955+
941956
TypeRef derivedTypeRef = EnqueueType(derivedType, typeToGenerate.Mode);
942957

943958
object? typeDiscriminator = null;
@@ -1021,6 +1036,211 @@ namedUnionType is not null &&
10211036
}
10221037
}
10231038

1039+
/// <summary>
1040+
/// Source-gen-side resolver: closes <paramref name="unboundDerived"/> against
1041+
/// <paramref name="baseType"/> via structural unification at compile time.
1042+
/// Returns <c>true</c> when the registration can be closed to a unique concrete
1043+
/// type. Returns <c>false</c> with a localized <paramref name="failureReason"/>
1044+
/// suitable for inclusion in a diagnostic when the derived type cannot be
1045+
/// resolved against this particular base.
1046+
///
1047+
/// IMPORTANT: This implementation MIRRORS the reflection resolver
1048+
/// <c>DefaultJsonTypeInfoResolver.Helpers.TryResolveOpenGenericDerivedType</c>
1049+
/// in src/System/Text/Json/Serialization/Metadata/DefaultJsonTypeInfoResolver.Helpers.cs.
1050+
/// Both implementations -- the structural unbound pre-check, the per-ancestor
1051+
/// unification, and the ambiguity detection -- must be kept in lockstep so that
1052+
/// source-gen and reflection produce the same closed type for the same registration.
1053+
/// Any algorithmic change here must be applied in the reflection mirror as well.
1054+
/// </summary>
1055+
private bool TryResolveOpenGenericDerivedType(
1056+
INamedTypeSymbol unboundDerived,
1057+
ITypeSymbol baseType,
1058+
out INamedTypeSymbol? resolvedType,
1059+
out string? failureReason)
1060+
{
1061+
resolvedType = null;
1062+
failureReason = null;
1063+
1064+
if (baseType is not INamedTypeSymbol { IsGenericType: true } constructedBase)
1065+
{
1066+
failureReason = SR.Polymorphism_OpenGeneric_Reason_NotAssignable;
1067+
return false;
1068+
}
1069+
1070+
INamedTypeSymbol derivedDefinition = unboundDerived.OriginalDefinition;
1071+
INamedTypeSymbol baseDefinition = constructedBase.OriginalDefinition;
1072+
1073+
// Collect every ancestor of the derived type definition whose original
1074+
// definition matches the base type definition. For classes there is at
1075+
// most one ancestor; for interfaces a derived type can implement the same
1076+
// interface definition multiple times with different type arguments.
1077+
List<INamedTypeSymbol> matchingBases = derivedDefinition
1078+
.GetCompatibleGenericBaseTypes(baseDefinition)
1079+
.ToList();
1080+
1081+
if (matchingBases.Count == 0)
1082+
{
1083+
failureReason = SR.Polymorphism_OpenGeneric_Reason_NotAssignable;
1084+
return false;
1085+
}
1086+
1087+
// The full set of generic parameters we must bind includes the parameters
1088+
// of the derived type definition AS WELL AS those declared by enclosing
1089+
// generic types (Outer<T>.Derived needs T bound from Outer).
1090+
List<ITypeParameterSymbol> requiredParams = derivedDefinition.GetAllTypeParameters();
1091+
ImmutableArray<ITypeSymbol> constructedBaseArgs = constructedBase.TypeArguments;
1092+
1093+
// Structural unbound pre-check: every required parameter must appear at least
1094+
// once somewhere in some matching ancestor's type arguments. If a parameter
1095+
// never appears at all, no closed base could ever bind it -- the derived
1096+
// definition is malformed regardless of which closed base it is registered
1097+
// against.
1098+
HashSet<ITypeParameterSymbol> referencedParams = new(SymbolEqualityComparer.Default);
1099+
foreach (INamedTypeSymbol mb in matchingBases)
1100+
{
1101+
foreach (ITypeSymbol arg in mb.TypeArguments)
1102+
{
1103+
CollectReferencedParameters(arg, referencedParams);
1104+
}
1105+
}
1106+
foreach (ITypeParameterSymbol required in requiredParams)
1107+
{
1108+
if (!referencedParams.Contains(required))
1109+
{
1110+
failureReason = string.Format(
1111+
CultureInfo.InvariantCulture,
1112+
SR.Polymorphism_OpenGeneric_Reason_UnboundParameter,
1113+
required.Name);
1114+
return false;
1115+
}
1116+
}
1117+
1118+
Dictionary<ITypeParameterSymbol, ITypeSymbol>? successfulSubstitution = null;
1119+
int successCount = 0;
1120+
1121+
foreach (INamedTypeSymbol matchingBase in matchingBases)
1122+
{
1123+
ImmutableArray<ITypeSymbol> matchingBaseArgs = matchingBase.TypeArguments;
1124+
Debug.Assert(matchingBaseArgs.Length == constructedBaseArgs.Length,
1125+
"matchingBase and constructedBase share the same generic definition, so arity must match.");
1126+
1127+
var substitution = new Dictionary<ITypeParameterSymbol, ITypeSymbol>(requiredParams.Count, SymbolEqualityComparer.Default);
1128+
bool unified = true;
1129+
for (int i = 0; i < matchingBaseArgs.Length; i++)
1130+
{
1131+
if (!matchingBaseArgs[i].TryUnifyWith(constructedBaseArgs[i], substitution))
1132+
{
1133+
unified = false;
1134+
break;
1135+
}
1136+
}
1137+
1138+
if (!unified)
1139+
{
1140+
continue;
1141+
}
1142+
1143+
// Unification succeeded for every position. Every required parameter must be
1144+
// bound; otherwise the resulting closed type would have unbound type arguments
1145+
// (an unspeakable type). A sibling ancestor may still bind this parameter, so
1146+
// failure here is not fatal -- just move on to the next matching ancestor.
1147+
bool allBound = true;
1148+
foreach (ITypeParameterSymbol p in requiredParams)
1149+
{
1150+
if (!substitution.ContainsKey(p))
1151+
{
1152+
allBound = false;
1153+
break;
1154+
}
1155+
}
1156+
1157+
if (!allBound)
1158+
{
1159+
continue;
1160+
}
1161+
1162+
successCount++;
1163+
if (successCount == 1)
1164+
{
1165+
successfulSubstitution = substitution;
1166+
}
1167+
else
1168+
{
1169+
failureReason = SR.Polymorphism_OpenGeneric_Reason_AmbiguousMatch;
1170+
return false;
1171+
}
1172+
}
1173+
1174+
if (successCount == 0 || successfulSubstitution is null)
1175+
{
1176+
failureReason = SR.Polymorphism_OpenGeneric_Reason_UnificationFailed;
1177+
return false;
1178+
}
1179+
1180+
// Validate constraints up front so the generated code will compile.
1181+
// Note: HasNotNullConstraint is not enforced because `notnull` is not a
1182+
// runtime-enforced constraint. Reflection MakeGenericType accepts e.g.
1183+
// string? for `where T : notnull`; source-gen must match that behavior.
1184+
if (!_knownSymbols.Compilation.TryValidateGenericConstraints(requiredParams, successfulSubstitution, out ITypeParameterSymbol? failedParam, out ITypeSymbol? failedArg))
1185+
{
1186+
failureReason = string.Format(
1187+
CultureInfo.InvariantCulture,
1188+
SR.Polymorphism_OpenGeneric_Reason_ConstraintViolation,
1189+
failedParam.Name,
1190+
failedArg?.ToDisplayString() ?? string.Empty);
1191+
return false;
1192+
}
1193+
1194+
// Build closedArgs in declaration order using the merged substitution.
1195+
ITypeSymbol[] closedArgs = new ITypeSymbol[requiredParams.Count];
1196+
for (int i = 0; i < requiredParams.Count; i++)
1197+
{
1198+
closedArgs[i] = successfulSubstitution[requiredParams[i]];
1199+
}
1200+
1201+
// Note: ConstructWithEnclosingTypeArguments takes the parameters in the order
1202+
// they appear when listed as TypeParameters on the type and on its enclosing types.
1203+
// GetAllTypeParameters preserves that order (outer-to-inner, declaration order).
1204+
resolvedType = derivedDefinition.ConstructWithEnclosingTypeArguments(closedArgs);
1205+
return true;
1206+
}
1207+
1208+
private static void CollectReferencedParameters(ITypeSymbol pattern, HashSet<ITypeParameterSymbol> set)
1209+
{
1210+
switch (pattern)
1211+
{
1212+
case ITypeParameterSymbol tp:
1213+
set.Add(tp);
1214+
return;
1215+
1216+
case IArrayTypeSymbol array:
1217+
CollectReferencedParameters(array.ElementType, set);
1218+
return;
1219+
1220+
case IPointerTypeSymbol pointer:
1221+
CollectReferencedParameters(pointer.PointedAtType, set);
1222+
return;
1223+
1224+
case INamedTypeSymbol { IsGenericType: true } named:
1225+
// Walk ContainingType to collect type parameters declared on enclosing
1226+
// generic types (e.g. T in Outer<T>.Box<U>). Roslyn's TypeArguments is
1227+
// leaf-only, while the reflection mirror uses Type.GetGenericArguments()
1228+
// which flattens enclosing + leaf. Without this recursion, the unbound
1229+
// pre-check would spuriously reject patterns whose only reference to a
1230+
// type parameter lives in the enclosing type.
1231+
if (named.ContainingType is { IsGenericType: true } containing)
1232+
{
1233+
CollectReferencedParameters(containing, set);
1234+
}
1235+
1236+
foreach (ITypeSymbol arg in named.TypeArguments)
1237+
{
1238+
CollectReferencedParameters(arg, set);
1239+
}
1240+
return;
1241+
}
1242+
}
1243+
10241244
/// <summary>
10251245
/// Checks whether the type is recognized as a union.
10261246
/// </summary>

src/libraries/System.Text.Json/gen/Resources/Strings.resx

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,4 +219,25 @@
219219
<data name="UnionTypeShapeNotSupportedMessageFormat" xml:space="preserve">
220220
<value>Union type '{0}' does not match the C# union shape convention: it must declare at least one public single-parameter case constructor and a public instance property named 'Value' with type 'object'.</value>
221221
</data>
222+
<data name="OpenGenericDerivedTypeCouldNotBeResolvedTitle" xml:space="preserve">
223+
<value>Open generic derived type could not be resolved for the polymorphic base type.</value>
224+
</data>
225+
<data name="OpenGenericDerivedTypeCouldNotBeResolvedMessageFormat" xml:space="preserve">
226+
<value>The open generic derived type '{0}' could not be resolved for the polymorphic base type '{1}': {2}.</value>
227+
</data>
228+
<data name="Polymorphism_OpenGeneric_Reason_NotAssignable" xml:space="preserve">
229+
<value>the derived type is not assignable to the base type</value>
230+
</data>
231+
<data name="Polymorphism_OpenGeneric_Reason_UnificationFailed" xml:space="preserve">
232+
<value>the base type's arguments do not match the derived type's base specification</value>
233+
</data>
234+
<data name="Polymorphism_OpenGeneric_Reason_UnboundParameter" xml:space="preserve">
235+
<value>the type parameter '{0}' of the derived type is not bound by the base type's arguments</value>
236+
</data>
237+
<data name="Polymorphism_OpenGeneric_Reason_ConstraintViolation" xml:space="preserve">
238+
<value>the constraint on type parameter '{0}' is not satisfied by '{1}'</value>
239+
</data>
240+
<data name="Polymorphism_OpenGeneric_Reason_AmbiguousMatch" xml:space="preserve">
241+
<value>the derived type matches the base type through multiple ancestors</value>
242+
</data>
222243
</root>

src/libraries/System.Text.Json/gen/Resources/xlf/Strings.cs.xlf

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,41 @@
132132
<target state="translated">Typ obsahuje více členů s komentářem JsonExtensionDataAttribute</target>
133133
<note />
134134
</trans-unit>
135+
<trans-unit id="OpenGenericDerivedTypeCouldNotBeResolvedMessageFormat">
136+
<source>The open generic derived type '{0}' could not be resolved for the polymorphic base type '{1}': {2}.</source>
137+
<target state="new">The open generic derived type '{0}' could not be resolved for the polymorphic base type '{1}': {2}.</target>
138+
<note />
139+
</trans-unit>
140+
<trans-unit id="Polymorphism_OpenGeneric_Reason_AmbiguousMatch">
141+
<source>the derived type matches the base type through multiple ancestors</source>
142+
<target state="new">the derived type matches the base type through multiple ancestors</target>
143+
<note />
144+
</trans-unit>
145+
<trans-unit id="Polymorphism_OpenGeneric_Reason_ConstraintViolation">
146+
<source>the constraint on type parameter '{0}' is not satisfied by '{1}'</source>
147+
<target state="new">the constraint on type parameter '{0}' is not satisfied by '{1}'</target>
148+
<note />
149+
</trans-unit>
150+
<trans-unit id="Polymorphism_OpenGeneric_Reason_NotAssignable">
151+
<source>the derived type is not assignable to the base type</source>
152+
<target state="new">the derived type is not assignable to the base type</target>
153+
<note />
154+
</trans-unit>
155+
<trans-unit id="Polymorphism_OpenGeneric_Reason_UnboundParameter">
156+
<source>the type parameter '{0}' of the derived type is not bound by the base type's arguments</source>
157+
<target state="new">the type parameter '{0}' of the derived type is not bound by the base type's arguments</target>
158+
<note />
159+
</trans-unit>
160+
<trans-unit id="Polymorphism_OpenGeneric_Reason_UnificationFailed">
161+
<source>the base type's arguments do not match the derived type's base specification</source>
162+
<target state="new">the base type's arguments do not match the derived type's base specification</target>
163+
<note />
164+
</trans-unit>
165+
<trans-unit id="OpenGenericDerivedTypeCouldNotBeResolvedTitle">
166+
<source>Open generic derived type could not be resolved for the polymorphic base type.</source>
167+
<target state="new">Open generic derived type could not be resolved for the polymorphic base type.</target>
168+
<note />
169+
</trans-unit>
135170
<trans-unit id="TypeContainsRefLikeMemberFormat">
136171
<source>The type '{0}' includes the ref like property, field or constructor parameter '{1}'. No source code will be generated for the property, field or constructor.</source>
137172
<target state="translated">Typ {0} zahrnuje ref-like parametr vlastnosti, pole nebo konstruktoru {1}. Pro vlastnost, pole nebo konstruktor se nevygeneruje žádný zdrojový kód.</target>

0 commit comments

Comments
 (0)