egametang/ET · error · NotSupportedException

{runtimeType.FullName} 缺少可用于导出的公共构造函数。必须通过构造函数提供的成员: {names}

Error message

{runtimeType.FullName} 缺少可用于导出的公共构造函数。必须通过构造函数提供的成员: {names}。请补充例如 [BsonConstructor] public {runtimeType.Name}({parameters})

What it means

Thrown by ResolveConstructor when emitting a complex object: the emitter needs a public constructor whose parameters (by name, case-insensitively, and by exact type) bind ALL serializable constructor-members, and either no constructor matched, or more than one matched without a [BsonConstructor] tiebreaker. The message lists the required member names and a suggested signature to copy.

Source

Thrown at Packages/cn.etetet.config/Scripts/Model/Share/CSharpObjectCodeEmitter.cs:414

                {
                    continue;
                }

                matches.Add(match);
            }

            ConstructorMatch attributeMatch = matches.SingleOrDefault(match => match.Constructor.GetCustomAttribute<BsonConstructorAttribute>() != null);
            if (attributeMatch != null)
            {
                return attributeMatch;
            }

            if (matches.Count == 1)
            {
                return matches[0];
            }

            throw new NotSupportedException(this.BuildMissingConstructorMessage(runtimeType, ctorMembers));
        }

        private bool TryMatchConstructor(ConstructorInfo constructor, List<SerializableMember> ctorMembers, out ConstructorMatch match)
        {
            match = null;

            ParameterInfo[] parameters = constructor.GetParameters();
            Dictionary<string, SerializableMember> membersByKey = this.BuildConstructorMemberLookup(ctorMembers);
            List<ConstructorArgument> arguments = new();
            HashSet<SerializableMember> boundMembers = new();

            foreach (ParameterInfo parameter in parameters)
            {
                if (!membersByKey.TryGetValue(parameter.Name ?? string.Empty, out SerializableMember member))
                {
                    if (parameter.IsOptional)
                    {
                        continue;

View on GitHub (pinned to 5cab01f7a8)

Solutions

  1. Add a public constructor whose parameters match the reported members by name (case-insensitive) and exact type, as suggested in the message.
  2. If multiple constructors qualify, annotate the canonical one with [BsonConstructor] so it is chosen unambiguously.
  3. Ensure parameter names align with member names or [BsonElement]/serialization names the emitter looks up.
  4. Remove or mark non-essential members [non-serialized] so the required member set shrinks to a ctor you already have.

Example fix

// before
public class Reward
{
    public int Id { get; set; }
    public int Count { get; set; }
    public Reward() { } // no matching ctor for {Id, Count}
}
// after
public class Reward
{
    public int Id { get; set; }
    public int Count { get; set; }
    [BsonConstructor]
    public Reward(int id, int count) { Id = id; Count = count; }
}
Defensive patterns

Strategy: type-guard

Validate before calling

// in a test, attempt emission of a sample instance to surface ctor issues early
var emitter = new CSharpObjectCodeEmitter();
try { emitter.Emit(sampleInstance); }
catch (NotSupportedException ex) { Assert.Fail(ex.Message); }

Type guard

static bool HasReconstructableCtor(Type t)
{
    // a public ctor exists whose parameter names/types cover all public settable members
    var memberNames = t.GetFields().Select(f => f.Name).ToHashSet(StringComparer.OrdinalIgnoreCase);
    return t.GetConstructors().Any(c =>
        c.GetParameters().All(p => p.IsOptional || memberNames.Contains(p.Name)));
}

Prevention

When it happens

Trigger: A serialized class has only a parameterless constructor but requires member-initialized reconstruction; the constructor parameter names do not match the member/serialization names; parameter types differ (e.g. member is long, ctor takes int); multiple candidate constructors exist and none is annotated with [BsonConstructor].

Common situations: Adding required (non-default) fields to a record-like config class without updating the constructor; renaming a member but not the matching ctor parameter; using init-only properties the emitter cannot supply via the ctor.

Related errors


AI-assisted analysis of egametang/ET@5cab01f7a8 (2026-08-13). Data as JSON: /api/errors/db59a882c5395486. Report an issue: GitHub.