Skip to content

Support parameter-less event methods in DataContractSerializer (without a StreamingContext parameter) #109264

Description

@Takym

When I use event methods for the DataContractSerializer system, I still need a StreamingContext parameter even though the BinaryFormatter stuff is deprecated. The constructor and state property of StreamingContext is marked as obsolete.

The StreamingContext _ parameter is unused in the below code. I feel it is redundant.

[OnSerializing()]
void OnSerializing(StreamingContext _) { ... }

[OnSerialized()]
void OnSerializing(StreamingContext _) { ... }

[OnDeserializing()]
void OnSerializing(StreamingContext _) { ... }

[OnDeserialized()]
void OnSerializing(StreamingContext _) { ... }

Parameter-less one like the code below makes me feel refreshed. Also, we do not need to type unused parameters. So, developing efficiency is more improved. In addition, maybe, passing (copying structure data) the parameter makes a program slow. Or maybe, the speed is the same because of checking parameters.

[OnSerializing()]
void OnSerializing() { ... }

[OnSerialized()]
void OnSerializing() { ... }

[OnDeserializing()]
void OnSerializing() { ... }

[OnDeserialized()]
void OnSerializing() { ... }

I agree that we should still support with StreamingContext _ parameter for compatibility but I want a parameter-less option.

Activity

  1. dotnet-policy-service commented on Oct 27, 2024

    @dotnet-policy-service
    Contributor

    Tagging subscribers to this area: @dotnet/area-system-text-json, @gregsdennis
    See info in area-owners.md if you want to be subscribed.

  2. Takym commented on Nov 23, 2024

    @Takym
    ContributorAuthor
    Related codes

    internal void EnsureMethodsImported()
    {
    if (!_isMethodChecked && UnderlyingType != null)
    {
    lock (this)
    {
    if (!_isMethodChecked)
    {
    Type type = UnderlyingType;
    MethodInfo[] methods = type.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
    for (int i = 0; i < methods.Length; i++)
    {
    MethodInfo method = methods[i];
    Type? prevAttributeType = null;
    ParameterInfo[] parameters = method.GetParameters();
    if (HasExtensionData && IsValidExtensionDataSetMethod(method, parameters))
    {
    if (method.Name == Globals.ExtensionDataSetExplicitMethod || !method.IsPublic)
    _extensionDataSetMethod = XmlFormatGeneratorStatics.ExtensionDataSetExplicitMethodInfo;
    else
    _extensionDataSetMethod = method;
    }
    if (IsValidCallback(method, parameters, Globals.TypeOfOnSerializingAttribute, _onSerializing, ref prevAttributeType))
    _onSerializing = method;
    if (IsValidCallback(method, parameters, Globals.TypeOfOnSerializedAttribute, _onSerialized, ref prevAttributeType))
    _onSerialized = method;
    if (IsValidCallback(method, parameters, Globals.TypeOfOnDeserializingAttribute, _onDeserializing, ref prevAttributeType))
    _onDeserializing = method;
    if (IsValidCallback(method, parameters, Globals.TypeOfOnDeserializedAttribute, _onDeserialized, ref prevAttributeType))
    _onDeserialized = method;
    }
    Interlocked.MemoryBarrier();
    _isMethodChecked = true;
    }
    }
    }
    }
    private bool IsValidExtensionDataSetMethod(MethodInfo method, ParameterInfo[] parameters)
    {
    if (method.Name == Globals.ExtensionDataSetExplicitMethod || method.Name == Globals.ExtensionDataSetMethod)
    {
    Debug.Assert(method.DeclaringType != null);
    if (_extensionDataSetMethod != null)
    ThrowInvalidDataContractException(SR.Format(SR.DuplicateExtensionDataSetMethod, method, _extensionDataSetMethod, DataContract.GetClrTypeFullName(method.DeclaringType)));
    if (method.ReturnType != Globals.TypeOfVoid)
    DataContract.ThrowInvalidDataContractException(SR.Format(SR.ExtensionDataSetMustReturnVoid, DataContract.GetClrTypeFullName(method.DeclaringType), method), method.DeclaringType);
    if (parameters == null || parameters.Length != 1 || parameters[0].ParameterType != Globals.TypeOfExtensionDataObject)
    DataContract.ThrowInvalidDataContractException(SR.Format(SR.ExtensionDataSetParameterInvalid, DataContract.GetClrTypeFullName(method.DeclaringType), method, Globals.TypeOfExtensionDataObject), method.DeclaringType);
    return true;
    }
    return false;
    }
    private static bool IsValidCallback(MethodInfo method, ParameterInfo[] parameters, Type attributeType, MethodInfo? currentCallback, ref Type? prevAttributeType)
    {
    if (method.IsDefined(attributeType, false))
    {
    Debug.Assert(method.DeclaringType != null);
    if (currentCallback != null)
    DataContract.ThrowInvalidDataContractException(SR.Format(SR.DuplicateCallback, method, currentCallback, DataContract.GetClrTypeFullName(method.DeclaringType), attributeType), method.DeclaringType);
    else if (prevAttributeType != null)
    DataContract.ThrowInvalidDataContractException(SR.Format(SR.DuplicateAttribute, prevAttributeType, attributeType, DataContract.GetClrTypeFullName(method.DeclaringType), method), method.DeclaringType);
    else if (method.IsVirtual)
    DataContract.ThrowInvalidDataContractException(SR.Format(SR.CallbacksCannotBeVirtualMethods, method, DataContract.GetClrTypeFullName(method.DeclaringType), attributeType), method.DeclaringType);
    else
    {
    if (method.ReturnType != Globals.TypeOfVoid)
    DataContract.ThrowInvalidDataContractException(SR.Format(SR.CallbackMustReturnVoid, DataContract.GetClrTypeFullName(method.DeclaringType), method), method.DeclaringType);
    if (parameters == null || parameters.Length != 1 || parameters[0].ParameterType != Globals.TypeOfStreamingContext)
    DataContract.ThrowInvalidDataContractException(SR.Format(SR.CallbackParameterInvalid, DataContract.GetClrTypeFullName(method.DeclaringType), method, Globals.TypeOfStreamingContext), method.DeclaringType);
    prevAttributeType = attributeType;
    }
    return true;
    }
    return false;
    }

    private static void InvokeOnSerializing(object obj, XmlObjectSerializerWriteContext context, ClassDataContract classContract)
    {
    if (classContract.BaseClassContract != null)
    InvokeOnSerializing(obj, context, classContract.BaseClassContract);
    if (classContract.OnSerializing != null)
    {
    var contextArg = context.GetStreamingContext();
    classContract.OnSerializing.Invoke(obj, new object[] { contextArg });
    }
    }
    private static void InvokeOnSerialized(object obj, XmlObjectSerializerWriteContext context, ClassDataContract classContract)
    {
    if (classContract.BaseClassContract != null)
    InvokeOnSerialized(obj, context, classContract.BaseClassContract);
    if (classContract.OnSerialized != null)
    {
    var contextArg = context.GetStreamingContext();
    classContract.OnSerialized.Invoke(obj, new object[] { contextArg });
    }
    }

    private void InvokeOnSerializing(ClassDataContract classContract)
    {
    if (classContract.BaseClassContract != null)
    InvokeOnSerializing(classContract.BaseClassContract);
    if (classContract.OnSerializing != null)
    {
    _ilg.LoadAddress(_objectLocal);
    _ilg.Load(_contextArg);
    _ilg.Call(XmlFormatGeneratorStatics.GetStreamingContextMethod);
    _ilg.Call(classContract.OnSerializing);
    }
    }
    private void InvokeOnSerialized(ClassDataContract classContract)
    {
    if (classContract.BaseClassContract != null)
    InvokeOnSerialized(classContract.BaseClassContract);
    if (classContract.OnSerialized != null)
    {
    _ilg.LoadAddress(_objectLocal);
    _ilg.Load(_contextArg);
    _ilg.Call(XmlFormatGeneratorStatics.GetStreamingContextMethod);
    _ilg.Call(classContract.OnSerialized);
    }
    }

    private void InvokeOnSerializing(ClassDataContract classContract)
    {
    if (classContract.BaseClassContract != null)
    InvokeOnSerializing(classContract.BaseClassContract);
    if (classContract.OnSerializing != null)
    {
    _ilg.LoadAddress(_objectLocal);
    _ilg.Load(_contextArg);
    _ilg.Call(XmlFormatGeneratorStatics.GetStreamingContextMethod);
    _ilg.Call(classContract.OnSerializing);
    }
    }
    private void InvokeOnSerialized(ClassDataContract classContract)
    {
    if (classContract.BaseClassContract != null)
    InvokeOnSerialized(classContract.BaseClassContract);
    if (classContract.OnSerialized != null)
    {
    _ilg.LoadAddress(_objectLocal);
    _ilg.Load(_contextArg);
    _ilg.Call(XmlFormatGeneratorStatics.GetStreamingContextMethod);
    _ilg.Call(classContract.OnSerialized);
    }
    }

    private static void InvokeOnDeserializing(XmlObjectSerializerReadContext context, ClassDataContract classContract, object obj)
    {
    if (classContract.BaseClassContract != null)
    InvokeOnDeserializing(context, classContract.BaseClassContract, obj);
    if (classContract.OnDeserializing != null)
    {
    var contextArg = context.GetStreamingContext();
    classContract.OnDeserializing.Invoke(obj, new object[] { contextArg });
    }
    }
    private static void InvokeOnDeserialized(XmlObjectSerializerReadContext context, ClassDataContract classContract, object obj)
    {
    if (classContract.BaseClassContract != null)
    InvokeOnDeserialized(context, classContract.BaseClassContract, obj);
    if (classContract.OnDeserialized != null)
    {
    var contextArg = context.GetStreamingContext();
    classContract.OnDeserialized.Invoke(obj, new object[] { contextArg });
    }
    }

    private void InvokeOnDeserializing(ClassDataContract classContract)
    {
    Debug.Assert(_objectLocal != null);
    Debug.Assert(_objectType != null);
    if (classContract.BaseClassContract != null)
    InvokeOnDeserializing(classContract.BaseClassContract);
    if (classContract.OnDeserializing != null)
    {
    _ilg.LoadAddress(_objectLocal);
    _ilg.ConvertAddress(_objectLocal.LocalType, _objectType);
    _ilg.Load(_contextArg);
    _ilg.LoadMember(XmlFormatGeneratorStatics.GetStreamingContextMethod);
    _ilg.Call(classContract.OnDeserializing);
    }
    }
    private void InvokeOnDeserialized(ClassDataContract classContract)
    {
    Debug.Assert(_objectLocal != null);
    Debug.Assert(_objectType != null);
    if (classContract.BaseClassContract != null)
    InvokeOnDeserialized(classContract.BaseClassContract);
    if (classContract.OnDeserialized != null)
    {
    _ilg.LoadAddress(_objectLocal);
    _ilg.ConvertAddress(_objectLocal.LocalType, _objectType);
    _ilg.Load(_contextArg);
    _ilg.LoadMember(XmlFormatGeneratorStatics.GetStreamingContextMethod);
    _ilg.Call(classContract.OnDeserialized);
    }
    }

    private void InvokeOnDeserializing(ClassDataContract classContract)
    {
    if (classContract.BaseClassContract != null)
    InvokeOnDeserializing(classContract.BaseClassContract);
    if (classContract.OnDeserializing != null)
    {
    _ilg.LoadAddress(_objectLocal);
    _ilg.ConvertAddress(_objectLocal.LocalType, _objectType!);
    _ilg.Load(_contextArg);
    _ilg.LoadMember(XmlFormatGeneratorStatics.GetStreamingContextMethod);
    _ilg.Call(classContract.OnDeserializing);
    }
    }
    private void InvokeOnDeserialized(ClassDataContract classContract)
    {
    if (classContract.BaseClassContract != null)
    InvokeOnDeserialized(classContract.BaseClassContract);
    if (classContract.OnDeserialized != null)
    {
    _ilg.LoadAddress(_objectLocal);
    _ilg.ConvertAddress(_objectLocal.LocalType, _objectType!);
    _ilg.Load(_contextArg);
    _ilg.LoadMember(XmlFormatGeneratorStatics.GetStreamingContextMethod);
    _ilg.Call(classContract.OnDeserialized);
    }
    }

  3. dotnet-policy-service commented on Feb 2, 2025

    @dotnet-policy-service
    Contributor

    Tagging subscribers to 'binaryformatter-migration': @adamsitnik, @bartonjs, @jeffhandley, @terrajobst

  4. added this to the Future milestone on Aug 3, 2025
  5. removed
    untriagedNew issue has not been triaged by the area owner
    on Aug 4, 2025
  6. added a commit that references this issue on Mar 22, 2026
    61e76b2
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions