Repository: asyncapi/modelina (ts)

Issue:
Change C# Equals() overload to allow reference type properties to be properly compared
### Why do we need this improvement?
We need this improvement because the current Equals() overload is functionally pointless. It only works if the properties of a given model are all value types or if the model and the object it is being compared with reference the same object in memory.

With the current behavior, the moment a JSON object is deserialized and typecasted to its Modelina model, it will always fail its comparison with an identical object if it contains a reference type which does not overload the "==" operator (which includes other models generated by Modelina).

### How will this change help?
This change would allow the Equals() function to be applicable in more situations.

This does not fix Equals() completely, as both the current implementation and this implementation proposed below would still fail to return the proper result in the case of Enumerable properties such as Array or List.

### How could it be implemented/designed?

By instead using the [Equals(Object, Object) function](https://learn.microsoft.com/en-us/dotnet/api/system.object.equals?view=net-8.0#system-object-equals(system-object-system-object)), we can:

- Implicitly perform a ReferenceEquals() on the compared properties.
- Return true if both properties are null (which may be the case if the properties are nullable).
- Fallback on the Equals() function of the compared properties.

### 🚧 Breaking changes
Yes

### 👀 Have you checked for similar open issues?
 I checked and didn't find a similar issue.
### 🏢 Have you read the Contributing Guidelines?
 I have read the [Contributing Guidelines](https://github.com/asyncapi/.github/blob/master/CONTRIBUTING.md)
### Are you willing to work on this issue?
Yes, I can work on this issue.

Pull request description:
fix: switch to Object.Equals for C# Equals override
## Description
<!-- Provide a brief description of the changes introduced by this pull request. -->

- Replaces `==` with `Object.Equals()` when overriding `Equals` for C# model generation
- Updates test related to the `Equals` override
- Adds a note regarding the limitations of the `Equals` override

## Related Issue
<!-- If this pull request is related to any existing issue, mention it here. -->
Fixes #1850 

## Checklist
- [x] The code follows the project's coding standards and is properly linted (`npm run lint`).
- [x] Tests have been added or updated to cover the changes.
- [x] Documentation has been updated to reflect the changes.
- [x] All tests pass successfully locally.(`npm run test`).

## Additional Notes
<!-- Add any additional information or context that might be relevant to reviewers. -->


Patch:
diff --git a/docs/languages/Csharp.md b/docs/languages/Csharp.md
index 965735b8..bf48c37e 100644
--- a/docs/languages/Csharp.md
+++ b/docs/languages/Csharp.md
@@ -61,13 +61,15 @@ Currently not supported, [let everyone know you need it](https://github.com/asyn
 
 ## Generate models with equals and GetHashCode methods
 
-To overwrite the `Equal` and `GetHashCode` methods, use the preset `CSHARP_COMMON_PRESET` and provide the options `equal: true` and `hashCode: true`
+To overwrite the `Equals` and `GetHashCode` methods, use the preset `CSHARP_COMMON_PRESET` and provide the options `equal: true` and `hashCode: true`
 
 Check out this [example for a live demonstration](../../examples/csharp-generate-equals-and-hashcode).
 
+Note that the `Equals` method uses `Object.Equals(objA, objB)`. This means that [reference types properties](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/reference-types) that do not override their default `Equals` method will perform a reference equality check to determine equality, returning `false` if they do not reference the same object.
+
 ## Generate models with auto-implemented properties
 
-To generate auto-implemented properties (the ones with with `{ get; set; }` accessors), use the preset `CSHARP_COMMON_PRESET` and provide the option `autoImplementedProperties: true`
+To generate auto-implemented properties (the ones with `{ get; set; }` accessors), use the preset `CSHARP_COMMON_PRESET` and provide the option `autoImplementedProperties: true`
 
 Check out this [example for a live demonstration](../../examples/csharp-auto-implemented-properties).
 
diff --git a/examples/csharp-generate-equals-and-hashcode/__snapshots__/index.spec.ts.snap b/examples/csharp-generate-equals-and-hashcode/__snapshots__/index.spec.ts.snap
index ba1e2ddf..b051f6d9 100644
--- a/examples/csharp-generate-equals-and-hashcode/__snapshots__/index.spec.ts.snap
+++ b/examples/csharp-generate-equals-and-hashcode/__snapshots__/index.spec.ts.snap
@@ -17,7 +17,7 @@ Array [
     if(obj is Root model)
     {
       if(ReferenceEquals(this, model)) { return true; }
-      return Email == model.Email;
+      return Object.Equals(Email, model.Email);
     }
 
     return false;
diff --git a/src/generators/csharp/presets/CommonPreset.ts b/src/generators/csharp/presets/CommonPreset.ts
index c1002306..2c23eb22 100644
--- a/src/generators/csharp/presets/CommonPreset.ts
+++ b/src/generators/csharp/presets/CommonPreset.ts
@@ -23,7 +23,7 @@ function renderEqual({
   let equalProperties = propertyKeys
     .map((propertyName) => {
       const accessorMethodProp = FormatHelpers.upperFirst(propertyName);
-      return `${accessorMethodProp} == model.${accessorMethodProp}`;
+      return `Object.Equals(${accessorMethodProp}, model.${accessorMethodProp})`;
     })
     .join(' && \n');
   equalProperties = `return ${


Test patch:
diff --git a/test/generators/csharp/presets/__snapshots__/CommonPreset.spec.ts.snap b/test/generators/csharp/presets/__snapshots__/CommonPreset.spec.ts.snap
index 633fa9fe..f62f475f 100644
--- a/test/generators/csharp/presets/__snapshots__/CommonPreset.spec.ts.snap
+++ b/test/generators/csharp/presets/__snapshots__/CommonPreset.spec.ts.snap
@@ -37,10 +37,10 @@ exports[`CSHARP_COMMON_PRESET should render Equals support function 1`] = `
     if(obj is Test model)
     {
       if(ReferenceEquals(this, model)) { return true; }
-      return StringProp == model.StringProp && 
-      NumberProp == model.NumberProp && 
-      ObjectProp == model.ObjectProp && 
-      AdditionalProperties == model.AdditionalProperties;
+      return Object.Equals(StringProp, model.StringProp) && 
+      Object.Equals(NumberProp, model.NumberProp) && 
+      Object.Equals(ObjectProp, model.ObjectProp) && 
+      Object.Equals(AdditionalProperties, model.AdditionalProperties);
     }
 
     return false;
@@ -81,8 +81,8 @@ exports[`CSHARP_COMMON_PRESET should render Equals support function 2`] = `
     if(obj is NestedTest model)
     {
       if(ReferenceEquals(this, model)) { return true; }
-      return StringProp == model.StringProp && 
-      AdditionalProperties == model.AdditionalProperties;
+      return Object.Equals(StringProp, model.StringProp) && 
+      Object.Equals(AdditionalProperties, model.AdditionalProperties);
     }
 
     return false;