diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/VersionConverter.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/VersionConverter.cs index e42d19bc01b9ff..ab9fbb74f70960 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/VersionConverter.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/VersionConverter.cs @@ -53,7 +53,7 @@ public override bool CanConvertTo(ITypeDescriptorContext? context, [NotNullWhen( if (value is Version version) { - return new Version(version.Major, version.Minor, version.Build, version.Revision); // return new instance + return version.Clone(); // return new instance } return base.ConvertFrom(context, culture, value); @@ -71,9 +71,28 @@ public override bool CanConvertTo(ITypeDescriptorContext? context, [NotNullWhen( { if (destinationType == typeof(InstanceDescriptor)) { - ConstructorInfo? ctor = typeof(Version).GetConstructor(BindingFlags.Public | BindingFlags.Instance, null, new Type[] { typeof(int), typeof(int), typeof(int), typeof(int) }, null); + ConstructorInfo? ctor; + object[] arguments; + + // Build and Revision are -1 when undefined, which the four-argument constructor rejects. + if (version.Build < 0) + { + ctor = typeof(Version).GetConstructor([typeof(int), typeof(int)]); + arguments = [version.Major, version.Minor]; + } + else if (version.Revision < 0) + { + ctor = typeof(Version).GetConstructor([typeof(int), typeof(int), typeof(int)]); + arguments = [version.Major, version.Minor, version.Build]; + } + else + { + ctor = typeof(Version).GetConstructor([typeof(int), typeof(int), typeof(int), typeof(int)]); + arguments = [version.Major, version.Minor, version.Build, version.Revision]; + } + Debug.Assert(ctor != null, "Couldn't find constructor"); - return new InstanceDescriptor(ctor, new object[] { version.Major, version.Minor, version.Build, version.Revision }); + return new InstanceDescriptor(ctor, arguments); } if (destinationType == typeof(string)) @@ -83,7 +102,7 @@ public override bool CanConvertTo(ITypeDescriptorContext? context, [NotNullWhen( if (destinationType == typeof(Version)) { - return new Version(version.Major, version.Minor, version.Build, version.Revision); + return version.Clone(); } } diff --git a/src/libraries/System.ComponentModel.TypeConverter/tests/VersionConverterTests.cs b/src/libraries/System.ComponentModel.TypeConverter/tests/VersionConverterTests.cs index 90067859c42fc6..5aa1548e94d64d 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/tests/VersionConverterTests.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/tests/VersionConverterTests.cs @@ -64,5 +64,41 @@ public static void ConvertFromInvalidVersion_WithContext_ThrowsFormatException(s Assert.Throws( () => VersionConverterTests.s_converter.ConvertFrom(TypeConverterTests.s_context, null, version)); } + + public static TheoryData Versions => new() + { + new Version(1, 2), + new Version(1, 2, 3), + new Version(1, 2, 3, 4), + }; + + [Theory] + [MemberData(nameof(Versions))] + public static void ConvertFrom_Version_ReturnsEqualCopy(Version version) + { + object result = s_converter.ConvertFrom(TypeConverterTests.s_context, null, version); + Assert.Equal(version, result); + Assert.NotSame(version, result); + } + + [Theory] + [MemberData(nameof(Versions))] + public static void ConvertTo_Version_ReturnsEqualCopy(Version version) + { + object result = s_converter.ConvertTo(TypeConverterTests.s_context, null, version, typeof(Version)); + Assert.Equal(version, result); + Assert.NotSame(version, result); + } + + [Theory] + [MemberData(nameof(Versions))] + public static void ConvertTo_InstanceDescriptor_RoundTrips(Version version) + { + InstanceDescriptor descriptor = Assert.IsType( + s_converter.ConvertTo(TypeConverterTests.s_context, null, version, typeof(InstanceDescriptor))); + + Assert.Equal(version, descriptor.Invoke()); + Assert.Equal(version, s_converter.ConvertFrom(TypeConverterTests.s_context, null, descriptor)); + } } }