Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
Takym opened this issue Oct 27, 2024 · 2 comments
Labels
area-Serialization untriaged New issue has not been triaged by the area owner

Comments

@Takym
Copy link
Contributor

Takym commented Oct 27, 2024

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.

@dotnet-policy-service dotnet-policy-service bot added the untriaged New issue has not been triaged by the area owner label Oct 27, 2024
Copy link
Contributor

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

@Takym
Copy link
Contributor Author

Takym commented Nov 23, 2024

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-Serialization untriaged New issue has not been triaged by the area owner
Projects
None yet
Development

No branches or pull requests

2 participants