private emitProperty()

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);
  }