44 using Microsoft . Web . Http ;
55 using System ;
66 using System . Collections . Concurrent ;
7+ using System . Collections . Generic ;
78 using System . Diagnostics . Contracts ;
89 using System . Linq ;
910 using System . Net . Http . Formatting ;
11+ using System . Net . Http . Headers ;
12+ using System . Reflection ;
1013 using static System . Linq . Expressions . Expression ;
1114 using static System . Net . Http . Headers . MediaTypeHeaderValue ;
1215 using static System . Reflection . BindingFlags ;
@@ -28,13 +31,7 @@ internal static Func<MediaTypeFormatter, MediaTypeFormatter> GetOrCreateCloneFun
2831 return cloneFunctions . GetOrAdd ( type , NewCloneFunction ) ;
2932 }
3033
31- static MediaTypeFormatter UseICloneable ( MediaTypeFormatter instance )
32- {
33- Contract . Requires ( instance != null ) ;
34- Contract . Ensures ( Contract . Result < MediaTypeFormatter > ( ) != null ) ;
35-
36- return ( MediaTypeFormatter ) ( ( ICloneable ) instance ) . Clone ( ) ;
37- }
34+ static MediaTypeFormatter UseICloneable ( MediaTypeFormatter instance ) => ( MediaTypeFormatter ) ( ( ICloneable ) instance ) . Clone ( ) ;
3835
3936 static Func < MediaTypeFormatter , MediaTypeFormatter > NewCloneFunction ( Type type )
4037 {
@@ -45,7 +42,7 @@ static Func<MediaTypeFormatter, MediaTypeFormatter> NewCloneFunction( Type type
4542 NewParameterlessConstructorActivator ( type ) ??
4643 throw new InvalidOperationException ( LocalSR . MediaTypeFormatterNotCloneable . FormatDefault ( type . Name , typeof ( ICloneable ) . Name ) ) ;
4744
48- return instance => CloneMediaTypes ( clone ( instance ) ) ;
45+ return instance => CloneMediaTypes ( clone ( instance ) , instance ) ;
4946 }
5047
5148 static Func < MediaTypeFormatter , MediaTypeFormatter > NewCopyConstructorActivator ( Type type )
@@ -67,7 +64,20 @@ static Func<MediaTypeFormatter, MediaTypeFormatter> NewCopyConstructorActivator(
6764 var @new = New ( constructor , Convert ( formatter , type ) ) ;
6865 var lambda = Lambda < Func < MediaTypeFormatter , MediaTypeFormatter > > ( @new , formatter ) ;
6966
70- return lambda . Compile ( ) ; // formatter => new MediaTypeFormatter( formatter );
67+ return ReinitializeSupportedMediaTypes ( lambda . Compile ( ) ) ; // formatter => new MediaTypeFormatter( formatter );
68+ }
69+
70+ static Func < MediaTypeFormatter , MediaTypeFormatter > ReinitializeSupportedMediaTypes ( Func < MediaTypeFormatter , MediaTypeFormatter > clone )
71+ {
72+ Contract . Requires ( clone != null ) ;
73+ Contract . Ensures ( Contract . Result < Func < MediaTypeFormatter , MediaTypeFormatter > > ( ) != null ) ;
74+
75+ return formatter =>
76+ {
77+ var instance = clone ( formatter ) ;
78+ SupportedMediaTypesInitializer . Initialize ( instance ) ;
79+ return instance ;
80+ } ;
7181 }
7282
7383 static Func < MediaTypeFormatter , MediaTypeFormatter > NewParameterlessConstructorActivator ( Type type )
@@ -92,21 +102,58 @@ static Func<MediaTypeFormatter, MediaTypeFormatter> NewParameterlessConstructorA
92102 return lambda . Compile ( ) ; // formatter => new MediaTypeFormatter();
93103 }
94104
95- static MediaTypeFormatter CloneMediaTypes ( MediaTypeFormatter instance )
105+ static MediaTypeFormatter CloneMediaTypes ( MediaTypeFormatter target , MediaTypeFormatter source )
96106 {
97- Contract . Requires ( instance != null ) ;
107+ Contract . Requires ( target != null ) ;
108+ Contract . Requires ( source != null ) ;
98109 Contract . Ensures ( Contract . Result < MediaTypeFormatter > ( ) != null ) ;
99110
100- var mediaTypes = instance . SupportedMediaTypes . ToArray ( ) ;
111+ target . SupportedMediaTypes . Clear ( ) ;
101112
102- instance . SupportedMediaTypes . Clear ( ) ;
113+ foreach ( var mediaType in source . SupportedMediaTypes )
114+ {
115+ target . SupportedMediaTypes . Add ( Parse ( mediaType . ToString ( ) ) ) ;
116+ }
117+
118+ return target ;
119+ }
120+
121+ /// <summary>
122+ /// Supports cloning with a copy constructor.
123+ /// </summary>
124+ /// <remarks>
125+ /// The <see cref="MediaTypeFormatter"/> copy constructor does not clone the SupportedMediaTypes property or backing field.
126+ /// <seealso cref="!:https://github.com/ASP-NET-MVC/aspnetwebstack/blob/4e40cdef9c8a8226685f95ef03b746bc8322aa92/src/System.Net.Http.Formatting/Formatting/MediaTypeFormatter.cs#L62"/>
127+ /// </remarks>
128+ static class SupportedMediaTypesInitializer
129+ {
130+ static FieldInfo field ;
131+ static PropertyInfo property ;
132+ static readonly ConstructorInfo newCollection ;
103133
104- foreach ( var mediaType in mediaTypes )
134+ static SupportedMediaTypesInitializer ( )
105135 {
106- instance . SupportedMediaTypes . Add ( Parse ( mediaType . ToString ( ) ) ) ;
136+ var flags = Public | NonPublic | Instance ;
137+ var mediaTypeFormatter = typeof ( MediaTypeFormatter ) ;
138+
139+ field = mediaTypeFormatter . GetField ( "_supportedMediaTypes" , flags ) ;
140+ property = mediaTypeFormatter . GetProperty ( nameof ( MediaTypeFormatter . SupportedMediaTypes ) , flags ) ;
141+ newCollection = mediaTypeFormatter . GetNestedType ( "MediaTypeHeaderValueCollection" , flags ) . GetConstructors ( flags ) . Single ( ) ;
107142 }
108143
109- return instance ;
144+ internal static void Initialize ( MediaTypeFormatter instance )
145+ {
146+ var list = new List < MediaTypeHeaderValue > ( ) ;
147+ var collection = newCollection . Invoke ( new object [ ] { list } ) ;
148+
149+ // the _supportedMediaTypes field is "readonly", which is why we must use Reflection instead of compiling an expression;
150+ // interestingly, the Reflection API lets us break rules that expression compilation does not
151+ field . SetValue ( instance , list ) ;
152+
153+ // since the value for the SupportedMediaTypes property comes from the backing field, we must do this here, even
154+ // though it's possible to set this property with a compiled expression
155+ property . SetMethod . Invoke ( instance , new object [ ] { collection } ) ;
156+ }
110157 }
111158 }
112159}
0 commit comments