in packages/jsii-pacmak/lib/targets/dotnet/dotnetgenerator.ts [1010:1146]
private emitProperty(
cls: spec.Type,
prop: spec.Property,
definingType: spec.Type,
datatype = false,
proxy = false,
): void {
this.emitNewLineIfNecessary();
const className = this.typeresolver.toNativeFqn(cls.fqn);
const access = this.renderAccessLevel(prop);
const staticKeyWord = prop.static ? 'static ' : '';
const propName = this.nameutils.convertPropertyName(prop.name);
const propTypeFQN = this.typeresolver.toDotNetType(prop.type);
const isOptional = prop.optional ? '?' : '';
// We need to use a backing field so we can perform type checking if the property type is a union, and this is a struct.
const backingFieldName =
spec.isInterfaceType(cls) && datatype && containsUnionType(prop.type)
? // We down-case the first letter, private fields are conventionally named with a _ prefix, and a camelCase name.
`_${propName.replace(/[A-Z]/, (c) => c.toLowerCase())}`
: undefined;
if (backingFieldName != null) {
this.code.line(
`private ${propTypeFQN}${isOptional} ${backingFieldName};`,
);
this.code.line();
}
this.dotnetDocGenerator.emitDocs(prop, {
api: 'member',
fqn: definingType.fqn,
memberName: prop.name,
});
if (prop.optional) {
this.code.line('[JsiiOptional]');
}
this.dotnetRuntimeGenerator.emitAttributesForProperty(prop);
let isOverrideKeyWord = '';
let isVirtualKeyWord = '';
let isAbstractKeyword = '';
// If the prop parent is a class
if (spec.isClassType(cls)) {
const implementedInBase = this.isMemberDefinedOnAncestor(
cls as spec.ClassType,
prop,
);
if (implementedInBase || datatype || proxy) {
// Override if the property is in a datatype or proxy class or declared in a parent class. If the member is
// static, use the "new" keyword instead, to indicate we are intentionally hiding the ancestor declaration (as
// C# does not inherit statics, they can be hidden but not overridden).The "new" keyword is optional in this
// context, but helps clarify intention.
isOverrideKeyWord = prop.static ? 'new ' : 'override ';
} else if (prop.abstract) {
// Abstract members get decorated as such
isAbstractKeyword = 'abstract ';
} else if (!prop.static && !implementedInBase) {
// Virtual if the prop is not static, and is not implemented in base member, this way we can later override it.
isVirtualKeyWord = 'virtual ';
}
}
const statement = `${access} ${isAbstractKeyword}${isVirtualKeyWord}${staticKeyWord}${isOverrideKeyWord}${propTypeFQN}${isOptional} ${propName}`;
this.code.openBlock(statement);
// Emit getters
if (backingFieldName != null) {
this.code.line(`get => ${backingFieldName};`);
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
} else if (datatype || prop.const || prop.abstract) {
this.code.line('get;');
} else {
// If the property is non-optional, add a bang to silence compiler warning
const bang = prop.optional ? '' : '!';
if (prop.static) {
this.code.line(
`get => GetStaticProperty<${propTypeFQN}${isOptional}>(typeof(${className}))${bang};`,
);
} else {
this.code.line(
`get => GetInstanceProperty<${propTypeFQN}${isOptional}>()${bang};`,
);
}
}
// Emit setters
const reflectCls = this.reflectAssembly.findType(cls.fqn) as
| reflect.ClassType
| reflect.InterfaceType;
const syntheticParam = new reflect.Parameter(
reflectCls.system,
reflectCls,
new reflect.Method(
reflectCls.system,
reflectCls.assembly,
reflectCls,
reflectCls,
{ name: '<synthetic>' },
),
{
name: 'value',
type: prop.type,
optional: prop.optional,
},
);
if (backingFieldName) {
this.code.openBlock('set');
this.emitUnionParameterValdation([syntheticParam], { noMangle: true });
this.code.line(`${backingFieldName} = value;`);
this.code.closeBlock();
} else if (datatype || (!prop.immutable && prop.abstract)) {
this.code.line('set;');
} else {
if (!prop.immutable) {
const setCode = prop.static
? `SetStaticProperty(typeof(${className}), value);`
: 'SetInstanceProperty(value);';
if (containsUnionType(prop.type)) {
this.code.openBlock('set');
this.emitUnionParameterValdation([syntheticParam], {
noMangle: true,
});
this.code.line(setCode);
this.code.closeBlock();
} else {
this.code.line(`set => ${setCode}`);
}
}
}
this.code.closeBlock();
this.flagFirstMemberWritten(true);
}