You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C#:序列化内部/受保护变量并调用非空基类构造函数方案咨询

Awesome question—this exact scenario comes up all the time when building constrained .NET class libraries, especially with MVVM and strict access control. Let’s break down some practical solutions that hit all your requirements:

1. Fix Your Custom ISerializable Implementation (No Third-Party Libraries)

Your existing reflection-based approach is on the right track—it just needs to be updated to include non-public members. The key is using BindingFlags to retrieve internal, protected, and private properties/fields.

Here's how to modify your saveableProperties and saveableFields methods:

private List<PropertyInfo> saveableProperties()
{
    List<PropertyInfo> saveables = new List<PropertyInfo>();
    // Include public AND non-public instance properties
    PropertyInfo[] props = typeof(SerializeClassDemo).GetProperties(
        BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
    foreach (PropertyInfo prop in props)
    {
        SaveAttribute attrs = prop.GetCustomAttribute<SaveAttribute>();
        if (attrs != null)
        {
            saveables.Add(prop);
        }
    }
    return saveables;
}

private List<FieldInfo> saveableFields()
{
    List<FieldInfo> saveables = new List<FieldInfo>();
    // Include public AND non-public instance fields (including readonly!)
    FieldInfo[] fields = typeof(SerializeClassDemo).GetFields(
        BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
    foreach (FieldInfo field in fields)
    {
        SaveAttribute attrs = field.GetCustomAttribute<SaveAttribute>();
        if (attrs != null)
        {
            saveables.Add(field);
        }
    }
    return saveables;
}

This will now pick up internal, protected, and private members marked with your [Save] attribute. Since you're already using ISerializable, the deserialization constructor will run first (calling your base class's parameterized constructor if you chain it properly), then you populate the members via reflection—perfect for initializing readonly fields through the constructor and restoring other non-public state.

2. Use Newtonsoft.Json (Json.NET) with Custom Configuration

If you prefer a more maintainable, battle-tested solution without rolling your own reflection logic, Json.NET is a great fit (MIT-licensed, compatible with GPL-3). It supports:

  • Non-public member serialization
  • Explicit constructor invocation (including parameterized base class constructors)
  • Opt-in serialization via attributes

Step 1: Mark Your Constructor and Members

First, mark your parameterized base class constructor with [JsonConstructor] (even if it's non-public), and tag members you want to serialize with [JsonProperty] or your custom [Save] attribute:

public class BaseClass
{
    public readonly int RequiredValue;

    // Mark base constructor for deserialization
    [JsonConstructor]
    protected BaseClass(int requiredValue)
    {
        RequiredValue = requiredValue;
    }
}

internal class SerializeClassDemo : BaseClass
{
    [Save]
    internal string InternalData { get; set; }

    [Save]
    protected string ProtectedData { get; private set; }

    [JsonConstructor]
    internal SerializeClassDemo(int requiredValue, string internalData, string protectedData) 
        : base(requiredValue)
    {
        InternalData = internalData;
        ProtectedData = protectedData;
    }
}

Step 2: Create a Custom Contract Resolver

This resolver will enforce opt-in via your [Save] attribute and include non-public members:

public class SaveAttributeContractResolver : DefaultContractResolver
{
    protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
    {
        var property = base.CreateProperty(member, memberSerialization);
        
        // Only include members marked with [Save]
        if (member.GetCustomAttribute<SaveAttribute>() == null)
        {
            property.Ignored = true;
            return property;
        }

        // Enable serialization/deserialization for non-public members
        if (member is PropertyInfo prop)
        {
            property.Readable = prop.GetGetMethod(true) != null;
            property.Writable = prop.GetSetMethod(true) != null;
        }
        else if (member is FieldInfo field)
        {
            property.Readable = true;
            property.Writable = !field.IsInitOnly; // Allow writing to non-readonly fields
        }

        return property;
    }
}

Step 3: Serialize/Deserialize with Configured Settings

var settings = new JsonSerializerSettings
{
    ContractResolver = new SaveAttributeContractResolver(),
    ConstructorHandling = ConstructorHandling.AllowNonPublicDefaultConstructor // Or use Auto if needed
};

// Serialization
string json = JsonConvert.SerializeObject(yourDemoInstance, settings);

// Deserialization
var restoredInstance = JsonConvert.DeserializeObject<SerializeClassDemo>(json, settings);

Json.NET will automatically call your parameterized constructor (and chain to the base class constructor) during deserialization, then populate the marked non-public members.

3. Protobuf-Net (For Binary Serialization)

If you need compact binary serialization instead of JSON, protobuf-net (Apache 2.0-licensed, compatible with GPL-3) works great. It supports non-public members and explicit constructor invocation:

  • Mark classes with [ProtoContract]
  • Mark members with [ProtoMember] (regardless of access level)
  • Mark parameterized constructors with [ProtoConstructor]

Example:

[ProtoContract]
public class BaseClass
{
    public readonly int RequiredValue;

    [ProtoConstructor]
    protected BaseClass(int requiredValue)
    {
        RequiredValue = requiredValue;
    }
}

[ProtoContract]
internal class SerializeClassDemo : BaseClass
{
    [ProtoMember(1)]
    internal string InternalData { get; set; }

    [ProtoMember(2)]
    protected string ProtectedData { get; private set; }

    [ProtoConstructor]
    internal SerializeClassDemo(int requiredValue, string internalData, string protectedData) 
        : base(requiredValue)
    {
        InternalData = internalData;
        ProtectedData = protectedData;
    }
}

Serialization/deserialization is straightforward:

using var stream = new MemoryStream();
Serializer.Serialize(stream, yourDemoInstance);
stream.Position = 0;
var restoredInstance = Serializer.Deserialize<SerializeClassDemo>(stream);

Which Should You Choose?

  • Fix your ISerializable code: Best if you want to avoid third-party libraries and stick with .NET's built-in serialization.
  • Json.NET: Most flexible for human-readable serialization, with excellent support for edge cases.
  • Protobuf-net: Best for high-performance, compact binary serialization.

All three solutions meet your requirements: handling non-public members, calling parameterized base class constructors, and working within MVVM/access level constraints.

内容的提问来源于stack exchange,提问作者Andrew Baumher

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.11 09:27:08