├── .gitignore ├── README.md ├── TypeConverter.NuGet ├── NuGet.config ├── NuGet.exe ├── NuGetPackage.ps1 ├── NuGetSetup.ps1 ├── Package.nuspec ├── Properties │ └── AssemblyInfo.cs ├── TypeConverter.NuGet.csproj ├── TypeConverterIcon.png ├── UpdateAssemblyInfo.ps1 └── tools │ ├── init.ps1 │ ├── install.ps1 │ └── uninstall.ps1 ├── TypeConverter.Tests ├── App.config ├── Caching │ └── CacheManagerTests.cs ├── ConverterRegistryExtensionsTests.cs ├── ConverterRegistryTests.cs ├── Converters │ ├── StringToBoolConverterTests.cs │ ├── StringToDateTimeConverterTests.cs │ ├── StringToDateTimeOffsetConverterTests.cs │ ├── StringToDecimalConverterTests.cs │ ├── StringToDoubleConverterTests.cs │ ├── StringToFloatConverterTests.cs │ ├── StringToGuidConverterTests.cs │ ├── StringToIntegerConverterTests.cs │ └── StringToUriConverterTests.cs ├── Extensions │ └── TypeExtensionsTests.cs ├── Properties │ └── AssemblyInfo.cs ├── Stubs │ ├── DerivedOperators.cs │ ├── MyEnum.cs │ ├── MyEnumConverter.cs │ ├── Operators.cs │ ├── Operators2.cs │ ├── OperatorsStruct.cs │ └── TestStruct1.cs ├── TypeConverter.Tests.csproj ├── TypeHelperTests.cs ├── Utils │ ├── CastTestRunner.cs │ ├── CompilerConversionTestCase.cs │ └── CompilerException.cs └── packages.config ├── TypeConverter.sln └── TypeConverter ├── Attempts ├── CastAttempt.cs ├── ChangeTypeAttempt.cs ├── CustomConvertAttempt.cs ├── EnumParseAttempt.cs ├── IConversionAttempt.cs ├── MapAttempt.cs └── StringParseAttempt.cs ├── Caching ├── CacheManager.cs └── CacheResult.cs ├── CastFlag.cs ├── CastResult.cs ├── ConversionResult.cs ├── ConverterRegistry.cs ├── Converters ├── StringToBoolConverter.cs ├── StringToDateTimeConverter.cs ├── StringToDateTimeOffsetConverter.cs ├── StringToDecimalConverter.cs ├── StringToDoubleConverter.cs ├── StringToFloatConverter.cs ├── StringToGuidConverter.cs ├── StringToIntegerConverter.cs ├── StringToUriConverter.cs └── ToStringFormattableConvertable.cs ├── Exceptions └── ConversionNotSupportedException.cs ├── Extensions ├── ConverterRegistryExtensions.cs └── TypeExtensions.cs ├── IConvertable.cs ├── IConverter.cs ├── IConverterCache.cs ├── IConverterRegistry.cs ├── Properties └── AssemblyInfo.cs ├── TypeConverter.csproj ├── TypeConverterKey.snk ├── TypeConverterPublicKey.snk ├── Utils ├── ReflectionHelper.cs └── TypeHelper.cs └── packages.config /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | x64/ 19 | x86/ 20 | bld/ 21 | [Bb]in/ 22 | [Oo]bj/ 23 | 24 | # Visual Studio 2015 cache/options directory 25 | .vs/ 26 | # Uncomment if you have tasks that create the project's static files in wwwroot 27 | #wwwroot/ 28 | 29 | # MSTest test Results 30 | [Tt]est[Rr]esult*/ 31 | [Bb]uild[Ll]og.* 32 | 33 | # NUNIT 34 | *.VisualState.xml 35 | TestResult.xml 36 | 37 | # Build Results of an ATL Project 38 | [Dd]ebugPS/ 39 | [Rr]eleasePS/ 40 | dlldata.c 41 | 42 | # DNX 43 | project.lock.json 44 | artifacts/ 45 | 46 | *_i.c 47 | *_p.c 48 | *_i.h 49 | *.ilk 50 | *.meta 51 | *.obj 52 | *.pch 53 | *.pdb 54 | *.pgc 55 | *.pgd 56 | *.rsp 57 | *.sbr 58 | *.tlb 59 | *.tli 60 | *.tlh 61 | *.tmp 62 | *.tmp_proj 63 | *.log 64 | *.vspscc 65 | *.vssscc 66 | .builds 67 | *.pidb 68 | *.svclog 69 | *.scc 70 | 71 | # Chutzpah Test files 72 | _Chutzpah* 73 | 74 | # Visual C++ cache files 75 | ipch/ 76 | *.aps 77 | *.ncb 78 | *.opendb 79 | *.opensdf 80 | *.sdf 81 | *.cachefile 82 | 83 | # Visual Studio profiler 84 | *.psess 85 | *.vsp 86 | *.vspx 87 | *.sap 88 | 89 | # TFS 2012 Local Workspace 90 | $tf/ 91 | 92 | # Guidance Automation Toolkit 93 | *.gpState 94 | 95 | # ReSharper is a .NET coding add-in 96 | _ReSharper*/ 97 | *.[Rr]e[Ss]harper 98 | *.DotSettings.user 99 | 100 | # JustCode is a .NET coding add-in 101 | .JustCode 102 | 103 | # TeamCity is a build add-in 104 | _TeamCity* 105 | 106 | # DotCover is a Code Coverage Tool 107 | *.dotCover 108 | 109 | # NCrunch 110 | _NCrunch_* 111 | .*crunch*.local.xml 112 | nCrunchTemp_* 113 | 114 | # MightyMoose 115 | *.mm.* 116 | AutoTest.Net/ 117 | 118 | # Web workbench (sass) 119 | .sass-cache/ 120 | 121 | # Installshield output folder 122 | [Ee]xpress/ 123 | 124 | # DocProject is a documentation generator add-in 125 | DocProject/buildhelp/ 126 | DocProject/Help/*.HxT 127 | DocProject/Help/*.HxC 128 | DocProject/Help/*.hhc 129 | DocProject/Help/*.hhk 130 | DocProject/Help/*.hhp 131 | DocProject/Help/Html2 132 | DocProject/Help/html 133 | 134 | # Click-Once directory 135 | publish/ 136 | 137 | # Publish Web Output 138 | *.[Pp]ublish.xml 139 | *.azurePubxml 140 | # TODO: Comment the next line if you want to checkin your web deploy settings 141 | # but database connection strings (with potential passwords) will be unencrypted 142 | *.pubxml 143 | *.publishproj 144 | 145 | # NuGet Packages 146 | *.nupkg 147 | # The packages folder can be ignored because of Package Restore 148 | **/packages/* 149 | # except build/, which is used as an MSBuild target. 150 | !**/packages/build/ 151 | # Uncomment if necessary however generally it will be regenerated when needed 152 | #!**/packages/repositories.config 153 | # NuGet v3's project.json files produces more ignoreable files 154 | *.nuget.props 155 | *.nuget.targets 156 | 157 | # Microsoft Azure Build Output 158 | csx/ 159 | *.build.csdef 160 | 161 | # Microsoft Azure Emulator 162 | ecf/ 163 | rcf/ 164 | 165 | # Microsoft Azure ApplicationInsights config file 166 | ApplicationInsights.config 167 | 168 | # Windows Store app package directory 169 | AppPackages/ 170 | BundleArtifacts/ 171 | 172 | # Visual Studio cache files 173 | # files ending in .cache can be ignored 174 | *.[Cc]ache 175 | # but keep track of directories ending in .cache 176 | !*.[Cc]ache/ 177 | 178 | # Others 179 | ClientBin/ 180 | ~$* 181 | *~ 182 | *.dbmdl 183 | *.dbproj.schemaview 184 | *.pfx 185 | *.publishsettings 186 | node_modules/ 187 | orleans.codegen.cs 188 | 189 | # RIA/Silverlight projects 190 | Generated_Code/ 191 | 192 | # Backup & report files from converting an old project file 193 | # to a newer Visual Studio version. Backup files are not needed, 194 | # because we have git ;-) 195 | _UpgradeReport_Files/ 196 | Backup*/ 197 | UpgradeLog*.XML 198 | UpgradeLog*.htm 199 | 200 | # SQL Server files 201 | *.mdf 202 | *.ldf 203 | 204 | # Business Intelligence projects 205 | *.rdl.data 206 | *.bim.layout 207 | *.bim_*.settings 208 | 209 | # Microsoft Fakes 210 | FakesAssemblies/ 211 | 212 | # GhostDoc plugin setting file 213 | *.GhostDoc.xml 214 | 215 | # Node.js Tools for Visual Studio 216 | .ntvs_analysis.dat 217 | 218 | # Visual Studio 6 build log 219 | *.plg 220 | 221 | # Visual Studio 6 workspace options file 222 | *.opt 223 | 224 | # Visual Studio LightSwitch build output 225 | **/*.HTMLClient/GeneratedArtifacts 226 | **/*.DesktopClient/GeneratedArtifacts 227 | **/*.DesktopClient/ModelManifest.xml 228 | **/*.Server/GeneratedArtifacts 229 | **/*.Server/ModelManifest.xml 230 | _Pvt_Extensions 231 | 232 | # Paket dependency manager 233 | .paket/paket.exe 234 | 235 | # FAKE - F# Make 236 | .fake/ 237 | *.bak 238 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # TypeConverter 2 | TypeConverter 3 | TypeConverter is a lightweight, portable class library which allows to convert between objects of different types. The philosophy behind TypeConverter is that type conversion should no longer be a painfull topic to developers. As a developer, you simply specify source and target type and pass in your desired object which you want to convert. 4 | 5 | This library is shipped with some basic sample conversion strategies, however, you are free to write your own type converters and register them in the IConverterRegistry. The most important type conversions provided by the .Net framework are integrated into TypeConverter. However, your own converters are always preferred over the .Net integrated default converstion/casting strategy. Following order of priority is respected: 6 | - Attempt 1: Try to convert using registered, user-defined IConverters 7 | - Attempt 2: Return source value if source and target type are the same 8 | - Attempt 3: Try to cast implicitly to the target type 9 | - Attempt 4: Try to cast explicitly to the target type 10 | - Attempt 5: Try to convert between string and enum value if either source or target type is an enum resp. a string 11 | - Attempt 6: Try to use String.Parse if either source or target type is a string 12 | 13 | If all attempts fail, the Convert method throws a ConversionNotSupportedException with the specified reason. TryConvert does return null if no conversion could be done. 14 | 15 | ### Download and Install TypeConverter 16 | This library is available on NuGet: https://www.nuget.org/packages/TypeConverter/ 17 | Use the following command to install TypeConverter using NuGet package manager console: 18 | 19 | PM> Install-Package TypeConverter 20 | 21 | You can use this library in any .Net project which is compatible to PCL (e.g. Xamarin Android, iOS, Windows Phone, Windows Store, Universal Apps, etc.) 22 | 23 | ### API Usage 24 | #### Create your own type converter 25 | If you want to implement a type converter, you simply implement the IConverter interface where TFrom is the generic type from which you want to convert and TTo is the type to which you want to convert to. 26 | Following sample code illustrates a converter which converts between string and System.Uri. 27 | ``` 28 | public class StringToUriConverter : IConverter, IConverter 29 | { 30 | public Uri Convert(string value) 31 | { 32 | return new Uri(value); 33 | } 34 | 35 | public string Convert(Uri value) 36 | { 37 | return value.AbsoluteUri; 38 | } 39 | } 40 | ``` 41 | 42 | #### Register a converter 43 | Create (or retrieve via dependency injection) an instance of ConverterRegistry and register those converters you like to use later on. Beware that you will have to register a converter for each direction you want to convert (if you support two-way conversion). Following example shows how to register the StringToUriConverter to convert between string and Uri and vice versa. 44 | ``` 45 | IConverterRegistry converterRegistry = new ConverterRegistry(); 46 | converterRegistry.RegisterConverter(() => new StringToUriConverter()); 47 | converterRegistry.RegisterConverter(() => new StringToUriConverter()); 48 | ``` 49 | 50 | #### Convert between types 51 | Now, after having set-up a basic converter, we can use IConverterRegistry to convert between object of different types. 52 | 53 | Convert from string to System.Uri 54 | ``` 55 | var uri = converterRegistry.Convert("http://github.com/"); 56 | ``` 57 | Convert from System.Uri to string 58 | ``` 59 | var uriAsString = converterRegistry.Convert(uri); 60 | ``` 61 | 62 | ### License 63 | TypeConverter is Copyright © 2018 [Thomas Galliker](https://ch.linkedin.com/in/thomasgalliker). Free for non-commercial use. For commercial use please contact the author. 64 | -------------------------------------------------------------------------------- /TypeConverter.NuGet/NuGet.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /TypeConverter.NuGet/NuGet.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thomasgalliker/TypeConverter/565afd5a018221a39bbf24e517f87cce683feaff/TypeConverter.NuGet/NuGet.exe -------------------------------------------------------------------------------- /TypeConverter.NuGet/NuGetPackage.ps1: -------------------------------------------------------------------------------- 1 | Param ( 2 | [switch]$Publish 3 | ) 4 | 5 | $ErrorActionPreference = "Stop" 6 | $global:ExitCode = 1 7 | 8 | function Write-Log { 9 | 10 | #region Parameters 11 | 12 | [cmdletbinding()] 13 | Param( 14 | [Parameter(ValueFromPipeline=$true)] 15 | [array] $Messages, 16 | 17 | [Parameter()] [ValidateSet("Error", "Warn", "Info")] 18 | [string] $Level = "Info", 19 | 20 | [Parameter()] 21 | [Switch] $NoConsoleOut = $false, 22 | 23 | [Parameter()] 24 | [String] $ForegroundColor = 'White', 25 | 26 | [Parameter()] [ValidateRange(1,30)] 27 | [Int16] $Indent = 0, 28 | 29 | [Parameter()] 30 | [IO.FileInfo] $Path = ".\NuGet.log", 31 | 32 | [Parameter()] 33 | [Switch] $Clobber, 34 | 35 | [Parameter()] 36 | [String] $EventLogName, 37 | 38 | [Parameter()] 39 | [String] $EventSource, 40 | 41 | [Parameter()] 42 | [Int32] $EventID = 1 43 | 44 | ) 45 | 46 | #endregion 47 | 48 | Begin {} 49 | 50 | Process { 51 | 52 | $ErrorActionPreference = "Continue" 53 | 54 | if ($Messages.Length -gt 0) { 55 | try { 56 | foreach($m in $Messages) { 57 | if ($NoConsoleOut -eq $false) { 58 | switch ($Level) { 59 | 'Error' { 60 | Write-Error $m -ErrorAction SilentlyContinue 61 | Write-Host ('{0}{1}' -f (" " * $Indent), $m) -ForegroundColor Red 62 | } 63 | 'Warn' { 64 | Write-Warning $m 65 | } 66 | 'Info' { 67 | Write-Host ('{0}{1}' -f (" " * $Indent), $m) -ForegroundColor $ForegroundColor 68 | } 69 | } 70 | } 71 | 72 | if ($m.Trim().Length -gt 0) { 73 | $msg = '{0}{1} [{2}] : {3}' -f (" " * $Indent), (Get-Date -Format "yyyy-MM-dd HH:mm:ss"), $Level.ToUpper(), $m 74 | 75 | if ($Clobber) { 76 | $msg | Out-File -FilePath $Path -Force 77 | } else { 78 | $msg | Out-File -FilePath $Path -Append 79 | } 80 | } 81 | 82 | if ($EventLogName) { 83 | 84 | if (-not $EventSource) { 85 | $EventSource = ([IO.FileInfo] $MyInvocation.ScriptName).Name 86 | } 87 | 88 | if(-not [Diagnostics.EventLog]::SourceExists($EventSource)) { 89 | [Diagnostics.EventLog]::CreateEventSource($EventSource, $EventLogName) 90 | } 91 | 92 | $log = New-Object System.Diagnostics.EventLog 93 | $log.set_log($EventLogName) 94 | $log.set_source($EventSource) 95 | 96 | switch ($Level) { 97 | "Error" { $log.WriteEntry($Message, 'Error', $EventID) } 98 | "Warn" { $log.WriteEntry($Message, 'Warning', $EventID) } 99 | "Info" { $log.WriteEntry($Message, 'Information', $EventID) } 100 | } 101 | } 102 | } 103 | } 104 | catch { 105 | throw "Failed to create log entry in: '$Path'. The error was: '$_'." 106 | } 107 | } 108 | } 109 | 110 | End {} 111 | 112 | <# 113 | .SYNOPSIS 114 | Writes logging information to screen and log file simultaneously. 115 | 116 | .DESCRIPTION 117 | Writes logging information to screen and log file simultaneously. Supports multiple log levels. 118 | 119 | .PARAMETER Messages 120 | The messages to be logged. 121 | 122 | .PARAMETER Level 123 | The type of message to be logged. 124 | 125 | .PARAMETER NoConsoleOut 126 | Specifies to not display the message to the console. 127 | 128 | .PARAMETER ConsoleForeground 129 | Specifies what color the text should be be displayed on the console. Ignored when switch 'NoConsoleOut' is specified. 130 | 131 | .PARAMETER Indent 132 | The number of spaces to indent the line in the log file. 133 | 134 | .PARAMETER Path 135 | The log file path. 136 | 137 | .PARAMETER Clobber 138 | Existing log file is deleted when this is specified. 139 | 140 | .PARAMETER EventLogName 141 | The name of the system event log, e.g. 'Application'. 142 | 143 | .PARAMETER EventSource 144 | The name to appear as the source attribute for the system event log entry. This is ignored unless 'EventLogName' is specified. 145 | 146 | .PARAMETER EventID 147 | The ID to appear as the event ID attribute for the system event log entry. This is ignored unless 'EventLogName' is specified. 148 | 149 | .EXAMPLE 150 | PS C:\> Write-Log -Message "It's all good!" -Path C:\MyLog.log -Clobber -EventLogName 'Application' 151 | 152 | .EXAMPLE 153 | PS C:\> Write-Log -Message "Oops, not so good!" -Level Error -EventID 3 -Indent 2 -EventLogName 'Application' -EventSource "My Script" 154 | 155 | .INPUTS 156 | System.String 157 | 158 | .OUTPUTS 159 | No output. 160 | 161 | .NOTES 162 | Revision History: 163 | 2011-03-10 : Andy Arismendi - Created. 164 | #> 165 | } 166 | 167 | function Create-Process() { 168 | param([string] $fileName, [string] $arguments) 169 | 170 | $pinfo = New-Object System.Diagnostics.ProcessStartInfo 171 | $pinfo.RedirectStandardError = $true 172 | $pinfo.RedirectStandardOutput = $true 173 | $pinfo.UseShellExecute = $false 174 | $pinfo.FileName = $fileName 175 | $pinfo.Arguments = $arguments 176 | 177 | $p = New-Object System.Diagnostics.Process 178 | $p.StartInfo = $pinfo 179 | 180 | return $p 181 | } 182 | 183 | function HandlePublishError { 184 | param([string] $ErrorMessage) 185 | 186 | # Run NuGet Setup 187 | $encodedMessage = [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($ErrorMessage)) 188 | $setupTask = Start-Process PowerShell.exe "-ExecutionPolicy Unrestricted -File .\NuGetSetup.ps1 -Url $url -Base64EncodedMessage $encodedMessage" -Wait -PassThru 189 | 190 | #Write-Log ("NuGet Setup Task Exit Code: " + $setupTask.ExitCode) 191 | 192 | if ($setupTask.ExitCode -eq 0) { 193 | # Try to push package again 194 | $publishTask = Create-Process .\NuGet.exe ("push " + $_.Name + " -Source " + $url) 195 | $publishTask.Start() | Out-Null 196 | $publishTask.WaitForExit() 197 | 198 | $output = ($publishTask.StandardOutput.ReadToEnd() -Split '[\r\n]') |? {$_} 199 | $error = (($publishTask.StandardError.ReadToEnd() -Split '[\r\n]') |? {$_}) 200 | Write-Log $output 201 | Write-Log $error Error 202 | 203 | if ($publishTask.ExitCode -eq 0) { 204 | $global:ExitCode = 0 205 | } 206 | } 207 | elseif ($setupTask.ExitCode -eq 2) { 208 | $global:ExitCode = 2 209 | } 210 | else { 211 | $global:ExitCode = 0 212 | } 213 | } 214 | 215 | function Publish { 216 | 217 | Write-Log " " 218 | Write-Log "Publishing package..." -ForegroundColor Green 219 | 220 | # Get nuget config 221 | [xml]$nugetConfig = Get-Content .\NuGet.Config 222 | 223 | $nugetConfig.configuration.packageSources.add | ForEach-Object { 224 | $url = $_.value 225 | 226 | Write-Log "Repository Url: $url" 227 | Write-Log " " 228 | 229 | Get-ChildItem *.nupkg | Where-Object { $_.Name.EndsWith(".symbols.nupkg") -eq $false } | ForEach-Object { 230 | 231 | # Try to push package 232 | $task = Create-Process .\NuGet.exe ("push " + $_.Name + " -Source " + $url) 233 | $task.Start() | Out-Null 234 | $task.WaitForExit() 235 | 236 | $output = ($task.StandardOutput.ReadToEnd() -Split '[\r\n]') |? { $_ } 237 | $error = ($task.StandardError.ReadToEnd() -Split '[\r\n]') |? { $_ } 238 | Write-Log $output 239 | Write-Log $error Error 240 | 241 | if ($task.ExitCode -gt 0) { 242 | HandlePublishError -ErrorMessage $error 243 | #Write-Log ("HandlePublishError() Exit Code: " + $global:ExitCode) 244 | } 245 | else { 246 | $global:ExitCode = 0 247 | } 248 | } 249 | } 250 | } 251 | 252 | Write-Log " " 253 | Write-Log "NuGet Packager 2.0.3" -ForegroundColor Yellow 254 | 255 | # Make sure the nuget executable is writable 256 | Set-ItemProperty NuGet.exe -Name IsReadOnly -Value $false 257 | 258 | # Make sure the nupkg files are writeable and create backup 259 | if (Test-Path *.nupkg) { 260 | Set-ItemProperty *.nupkg -Name IsReadOnly -Value $false 261 | 262 | Write-Log " " 263 | Write-Log "Creating backup..." -ForegroundColor Green 264 | 265 | Get-ChildItem *.nupkg | ForEach-Object { 266 | Move-Item $_.Name ($_.Name + ".bak") -Force 267 | Write-Log ("Renamed " + $_.Name + " to " + $_.Name + ".bak") 268 | } 269 | } 270 | 271 | Write-Log " " 272 | Write-Log "Updating NuGet..." -ForegroundColor Green 273 | Write-Log (Invoke-Command {.\NuGet.exe update -Self} -ErrorAction Stop) 274 | 275 | Write-Log " " 276 | Write-Log "Creating package..." -ForegroundColor Green 277 | 278 | # Create symbols package if any .pdb files are located in the lib folder 279 | If ((Get-ChildItem *.pdb -Path .\lib -Recurse).Count -gt 0) { 280 | $packageTask = Create-Process .\NuGet.exe ("pack Package.nuspec -Symbol -Verbosity Detailed") 281 | $packageTask.Start() | Out-Null 282 | $packageTask.WaitForExit() 283 | 284 | $output = ($packageTask.StandardOutput.ReadToEnd() -Split '[\r\n]') |? {$_} 285 | $error = (($packageTask.StandardError.ReadToEnd() -Split '[\r\n]') |? {$_}) 286 | Write-Log $output 287 | Write-Log $error Error 288 | 289 | $global:ExitCode = $packageTask.ExitCode 290 | } 291 | Else { 292 | $packageTask = Create-Process .\NuGet.exe ("pack Package.nuspec -Verbosity Detailed") 293 | $packageTask.Start() | Out-Null 294 | $packageTask.WaitForExit() 295 | 296 | $output = ($packageTask.StandardOutput.ReadToEnd() -Split '[\r\n]') |? {$_} 297 | $error = (($packageTask.StandardError.ReadToEnd() -Split '[\r\n]') |? {$_}) 298 | Write-Log $output 299 | Write-Log $error Error 300 | 301 | $global:ExitCode = $packageTask.ExitCode 302 | } 303 | 304 | # Check if package should be published 305 | if ($Publish -and $global:ExitCode -eq 0) { 306 | Publish 307 | } 308 | 309 | Write-Log " " 310 | Write-Log "Exit Code: $global:ExitCode" -ForegroundColor Gray 311 | 312 | $host.SetShouldExit($global:ExitCode) 313 | Exit $global:ExitCode -------------------------------------------------------------------------------- /TypeConverter.NuGet/NuGetSetup.ps1: -------------------------------------------------------------------------------- 1 | Param ( 2 | [string]$Url, 3 | [string]$Base64EncodedMessage 4 | ) 5 | 6 | $ErrorActionPreference = "Stop" 7 | $ExitCode = 1 8 | 9 | function Write-Log { 10 | 11 | #region Parameters 12 | 13 | [cmdletbinding()] 14 | Param( 15 | [Parameter(ValueFromPipeline=$true)] 16 | [array] $Messages, 17 | 18 | [Parameter()] [ValidateSet("Error", "Warn", "Info")] 19 | [string] $Level = "Info", 20 | 21 | [Parameter()] 22 | [Switch] $NoConsoleOut = $false, 23 | 24 | [Parameter()] 25 | [String] $ForegroundColor = 'White', 26 | 27 | [Parameter()] [ValidateRange(1,30)] 28 | [Int16] $Indent = 0, 29 | 30 | [Parameter()] 31 | [IO.FileInfo] $Path = ".\NuGet.log", 32 | 33 | [Parameter()] 34 | [Switch] $Clobber, 35 | 36 | [Parameter()] 37 | [String] $EventLogName, 38 | 39 | [Parameter()] 40 | [String] $EventSource, 41 | 42 | [Parameter()] 43 | [Int32] $EventID = 1 44 | 45 | ) 46 | 47 | #endregion 48 | 49 | Begin {} 50 | 51 | Process { 52 | 53 | $ErrorActionPreference = "Continue" 54 | 55 | if ($Messages.Length -gt 0) { 56 | try { 57 | foreach($m in $Messages) { 58 | if ($NoConsoleOut -eq $false) { 59 | switch ($Level) { 60 | 'Error' { 61 | Write-Error $m -ErrorAction SilentlyContinue 62 | Write-Host ('{0}{1}' -f (" " * $Indent), $m) -ForegroundColor Red 63 | } 64 | 'Warn' { 65 | Write-Warning $m 66 | } 67 | 'Info' { 68 | Write-Host ('{0}{1}' -f (" " * $Indent), $m) -ForegroundColor $ForegroundColor 69 | } 70 | } 71 | } 72 | 73 | if ($m.Trim().Length -gt 0) { 74 | $msg = '{0}{1} [{2}] : {3}' -f (" " * $Indent), (Get-Date -Format "yyyy-MM-dd HH:mm:ss"), $Level.ToUpper(), $m 75 | 76 | if ($Clobber) { 77 | $msg | Out-File -FilePath $Path -Force 78 | } else { 79 | $msg | Out-File -FilePath $Path -Append 80 | } 81 | } 82 | 83 | if ($EventLogName) { 84 | 85 | if (-not $EventSource) { 86 | $EventSource = ([IO.FileInfo] $MyInvocation.ScriptName).Name 87 | } 88 | 89 | if(-not [Diagnostics.EventLog]::SourceExists($EventSource)) { 90 | [Diagnostics.EventLog]::CreateEventSource($EventSource, $EventLogName) 91 | } 92 | 93 | $log = New-Object System.Diagnostics.EventLog 94 | $log.set_log($EventLogName) 95 | $log.set_source($EventSource) 96 | 97 | switch ($Level) { 98 | "Error" { $log.WriteEntry($Message, 'Error', $EventID) } 99 | "Warn" { $log.WriteEntry($Message, 'Warning', $EventID) } 100 | "Info" { $log.WriteEntry($Message, 'Information', $EventID) } 101 | } 102 | } 103 | } 104 | } 105 | catch { 106 | throw "Failed to create log entry in: '$Path'. The error was: '$_'." 107 | } 108 | } 109 | } 110 | 111 | End {} 112 | 113 | <# 114 | .SYNOPSIS 115 | Writes logging information to screen and log file simultaneously. 116 | 117 | .DESCRIPTION 118 | Writes logging information to screen and log file simultaneously. Supports multiple log levels. 119 | 120 | .PARAMETER Messages 121 | The messages to be logged. 122 | 123 | .PARAMETER Level 124 | The type of message to be logged. 125 | 126 | .PARAMETER NoConsoleOut 127 | Specifies to not display the message to the console. 128 | 129 | .PARAMETER ConsoleForeground 130 | Specifies what color the text should be be displayed on the console. Ignored when switch 'NoConsoleOut' is specified. 131 | 132 | .PARAMETER Indent 133 | The number of spaces to indent the line in the log file. 134 | 135 | .PARAMETER Path 136 | The log file path. 137 | 138 | .PARAMETER Clobber 139 | Existing log file is deleted when this is specified. 140 | 141 | .PARAMETER EventLogName 142 | The name of the system event log, e.g. 'Application'. 143 | 144 | .PARAMETER EventSource 145 | The name to appear as the source attribute for the system event log entry. This is ignored unless 'EventLogName' is specified. 146 | 147 | .PARAMETER EventID 148 | The ID to appear as the event ID attribute for the system event log entry. This is ignored unless 'EventLogName' is specified. 149 | 150 | .EXAMPLE 151 | PS C:\> Write-Log -Message "It's all good!" -Path C:\MyLog.log -Clobber -EventLogName 'Application' 152 | 153 | .EXAMPLE 154 | PS C:\> Write-Log -Message "Oops, not so good!" -Level Error -EventID 3 -Indent 2 -EventLogName 'Application' -EventSource "My Script" 155 | 156 | .INPUTS 157 | System.String 158 | 159 | .OUTPUTS 160 | No output. 161 | 162 | .NOTES 163 | Revision History: 164 | 2011-03-10 : Andy Arismendi - Created. 165 | #> 166 | } 167 | 168 | $choices = [System.Management.Automation.Host.ChoiceDescription[]]( 169 | (New-Object System.Management.Automation.Host.ChoiceDescription "&Add API Key","Add an API Key for this URL"), 170 | (New-Object System.Management.Automation.Host.ChoiceDescription "&Skip","Skip pushing to this URL")) 171 | 172 | Write-Output "" 173 | Write-Log "Invalid API key for this repository URL, or there is a version conflict" Warn 174 | 175 | If ($Base64EncodedMessage) { 176 | Write-Warning ([System.Text.Encoding]::UTF8.GetString([Convert]::FromBase64String($Base64EncodedMessage))) 177 | } 178 | 179 | $firstAnswer = $Host.UI.PromptForChoice(("Would you like to try adding an API key for " + $Url + "?"), "", $choices, (1)) 180 | 181 | if ($firstAnswer -eq 0) { 182 | $fields = new-object "System.Collections.ObjectModel.Collection``1[[System.Management.Automation.Host.FieldDescription]]" 183 | 184 | $f = New-Object System.Management.Automation.Host.FieldDescription "API Key for $Url" 185 | $f.SetParameterType( [System.Security.SecureString] ) 186 | $f.HelpMessage = "Please enter API Key for $Url" 187 | $f.Label = "&API Key for $Url" 188 | 189 | $fields.Add($f) 190 | 191 | $results = $Host.UI.Prompt( "Add API Key", "", $fields ) 192 | 193 | $pass = [Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR($results["API Key for $Url"])) 194 | 195 | # Add API Key to config file 196 | Write-Log (.\NuGet.exe setApiKey $pass -Source $Url) 197 | 198 | if ($LASTEXITCODE -le 0) { 199 | $ExitCode = 0 200 | } 201 | } 202 | else { 203 | Write-Log "Skipping..." 204 | $ExitCode = 2 205 | } 206 | 207 | $host.SetShouldExit($ExitCode) 208 | Exit $ExitCode -------------------------------------------------------------------------------- /TypeConverter.NuGet/Package.nuspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | TypeConverter 5 | 1.0.28-pre92 6 | TypeConverter 7 | Thomas Galliker 8 | http://opensource.org/licenses/Apache-2.0 9 | https://github.com/thomasgalliker/TypeConverter/ 10 | https://raw.githubusercontent.com/thomasgalliker/TypeConverter/master/TypeConverter.NuGet/TypeConverterIcon.png 11 | false 12 | 13 | TypeConverter is a lightweight, portable class library which allows to convert between objects of different types. 14 | 15 | 16 | TypeConverter is a lightweight, portable class library which allows to convert between objects of different types. 17 | This library is shipped with some basic sample converters, however, you are free to write your own type converters 18 | and register them in the IConverterRegistry. 19 | 20 | 21 | TypeConverter convert conversion type mapping mapper 22 | windows phone winphone wp8 wpa81 win81 windowsphone android xamarin.forms xamarin.ios ios xamarin.android monoandroid monodroid monotouch 23 | 24 | 25 | 1.0.28-pre 26 | - Added new converters 27 | - Added new RegisterConverter method in IConverterRegistry which allows to register non-generic IConvertable converters 28 | - Added new RegisterMapping method which allows to specify simple property-to-property mappings 29 | - Minor performance improvements and bug fixes 30 | 31 | 1.0.27 32 | - Complete refactoring of caching mechanism 33 | - New IConverterCache interface to interact with caching configuration 34 | - Cache size limit with read access weighting 35 | 36 | 1.0.26 37 | - Added ChangeType support (which uses Convert.ToXYZ(value) methods) 38 | 39 | 1.0.25 40 | - Improved support for implicit and explicit type casting 41 | - TryConvert API cleaned-up 42 | 43 | Copyright 2017 44 | en-US 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | -------------------------------------------------------------------------------- /TypeConverter.NuGet/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.InteropServices; 3 | 4 | // General Information about an assembly is controlled through the following 5 | // set of attributes. Change these attribute values to modify the information 6 | // associated with an assembly. 7 | 8 | [assembly: AssemblyTitle("TypeConverter.NuGet")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("Thomas Galliker")] 12 | [assembly: AssemblyProduct("TypeConverter.NuGet")] 13 | [assembly: AssemblyCopyright("Copyright © 2017")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | 21 | [assembly: ComVisible(false)] 22 | 23 | // The following GUID is for the ID of the typelib if this project is exposed to COM 24 | 25 | [assembly: Guid("5b586874-8d3e-4137-9ba8-e982413416d5")] 26 | 27 | // Version information for an assembly consists of the following four values: 28 | // 29 | // Major Version 30 | // Minor Version 31 | // Build Number 32 | // Revision 33 | // 34 | // You can specify all the values or you can default the Build and Revision Numbers 35 | // by using the '*' as shown below: 36 | // [assembly: AssemblyVersion("1.0.28")] 37 | 38 | [assembly: AssemblyVersion("1.0.28")] 39 | [assembly: AssemblyFileVersion("1.0.28-pre92")] 40 | -------------------------------------------------------------------------------- /TypeConverter.NuGet/TypeConverter.NuGet.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Debug 5 | AnyCPU 6 | 8.0.30703 7 | 2.0 8 | {2CF5E868-1098-4C6D-9001-27D8DC0D87BE} 9 | Library 10 | Properties 11 | TypeConverter.NuGet 12 | TypeConverter.NuGet 13 | v3.5 14 | 15 | 16 | 512 17 | 18 | 19 | AnyCPU 20 | true 21 | full 22 | false 23 | bin\Debug\ 24 | DEBUG;TRACE 25 | prompt 26 | 4 27 | 28 | 29 | AnyCPU 30 | pdbonly 31 | true 32 | bin\Release\ 33 | TRACE 34 | prompt 35 | 4 36 | 37 | 38 | AnyCPU 39 | bin\Debug\ 40 | 41 | 42 | AnyCPU 43 | bin\Release\ 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | Designer 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | -------------------------------------------------------------------------------- /TypeConverter.NuGet/TypeConverterIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thomasgalliker/TypeConverter/565afd5a018221a39bbf24e517f87cce683feaff/TypeConverter.NuGet/TypeConverterIcon.png -------------------------------------------------------------------------------- /TypeConverter.NuGet/UpdateAssemblyInfo.ps1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thomasgalliker/TypeConverter/565afd5a018221a39bbf24e517f87cce683feaff/TypeConverter.NuGet/UpdateAssemblyInfo.ps1 -------------------------------------------------------------------------------- /TypeConverter.NuGet/tools/init.ps1: -------------------------------------------------------------------------------- 1 | # Runs the first time a package is installed in a solution, and every time the solution is opened. 2 | 3 | param($installPath, $toolsPath, $package, $project) 4 | 5 | # $installPath is the path to the folder where the package is installed. 6 | # $toolsPath is the path to the tools directory in the folder where the package is installed. 7 | # $package is a reference to the package object. 8 | # $project is null in init.ps1 9 | -------------------------------------------------------------------------------- /TypeConverter.NuGet/tools/install.ps1: -------------------------------------------------------------------------------- 1 | # Runs every time a package is installed in a project 2 | 3 | param($installPath, $toolsPath, $package, $project) 4 | 5 | # $installPath is the path to the folder where the package is installed. 6 | # $toolsPath is the path to the tools directory in the folder where the package is installed. 7 | # $package is a reference to the package object. 8 | # $project is a reference to the project the package was installed to. 9 | -------------------------------------------------------------------------------- /TypeConverter.NuGet/tools/uninstall.ps1: -------------------------------------------------------------------------------- 1 | # Runs every time a package is uninstalled 2 | 3 | param($installPath, $toolsPath, $package, $project) 4 | 5 | # $installPath is the path to the folder where the package is installed. 6 | # $toolsPath is the path to the tools directory in the folder where the package is installed. 7 | # $package is a reference to the package object. 8 | # $project is a reference to the project the package was installed to. 9 | -------------------------------------------------------------------------------- /TypeConverter.Tests/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /TypeConverter.Tests/Caching/CacheManagerTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | using FluentAssertions; 5 | 6 | using TypeConverter.Attempts; 7 | using TypeConverter.Caching; 8 | 9 | using Xunit; 10 | 11 | namespace TypeConverter.Tests.Caching 12 | { 13 | public class ConverterCacheTests 14 | { 15 | [Fact] 16 | public void ShouldNotGetCachedValueWhenEmpty() 17 | { 18 | // Arrange 19 | var cacheManager = new CacheManager(); 20 | cacheManager.IsCacheEnabled = true; 21 | 22 | // Act 23 | var cacheResult = cacheManager.TryGetCachedValue(typeof(int), typeof(double)); 24 | 25 | // Assert 26 | cacheResult.ConversionAttempt.Should().BeNull(); 27 | cacheResult.IsCached.Should().BeFalse(); 28 | } 29 | 30 | [Fact] 31 | public void ShouldGetCachedValue() 32 | { 33 | // Arrange 34 | var cacheManager = new CacheManager(); 35 | cacheManager.IsCacheEnabled = true; 36 | 37 | // Act 38 | cacheManager.UpdateCache(typeof(int), typeof(double), isConvertable: true, conversionAttempt: new CastAttempt()); 39 | 40 | // Assert 41 | var cacheResult = cacheManager.TryGetCachedValue(typeof(int), typeof(double)); 42 | 43 | cacheResult.ConversionAttempt.Should().BeOfType(typeof(CastAttempt)); 44 | cacheResult.IsCached.Should().BeTrue(); 45 | } 46 | 47 | [Fact] 48 | public void ShouldNotGetCachedValueIfCachingIsDisabled() 49 | { 50 | // Arrange 51 | var cacheManager = new CacheManager(); 52 | cacheManager.IsCacheEnabled = true; 53 | cacheManager.UpdateCache(typeof(int), typeof(double), isConvertable: true, conversionAttempt: new CastAttempt()); 54 | cacheManager.IsCacheEnabled = false; 55 | 56 | // Act 57 | var cacheResult = cacheManager.TryGetCachedValue(typeof(int), typeof(double)); 58 | 59 | // Assert 60 | cacheResult.ConversionAttempt.Should().BeNull(); 61 | cacheResult.IsCached.Should().BeFalse(); 62 | } 63 | 64 | [Fact] 65 | public void ShouldResetCache() 66 | { 67 | // Arrange 68 | var cacheManager = new CacheManager(); 69 | cacheManager.IsCacheEnabled = true; 70 | 71 | cacheManager.UpdateCache(typeof(int), typeof(double), isConvertable: true, conversionAttempt: new CastAttempt()); 72 | 73 | var cacheResultBeforeReset = cacheManager.TryGetCachedValue(typeof(int), typeof(double)); 74 | 75 | // Act 76 | cacheManager.Reset(); 77 | 78 | // Assert 79 | var cacheResultAfterReset = cacheManager.TryGetCachedValue(typeof(int), typeof(double)); 80 | 81 | cacheResultBeforeReset.ConversionAttempt.Should().BeOfType(typeof(CastAttempt)); 82 | cacheResultBeforeReset.IsCached.Should().BeTrue(); 83 | 84 | cacheResultAfterReset.ConversionAttempt.Should().BeNull(); 85 | cacheResultAfterReset.IsCached.Should().BeFalse(); 86 | } 87 | 88 | [Fact] 89 | public void ShouldLimitCacheToMaxCacheSize() 90 | { 91 | // Arrange 92 | var cacheManager = new CacheManager(); 93 | cacheManager.IsCacheEnabled = true; 94 | cacheManager.MaxCacheSize = 3; 95 | cacheManager.IsMaxCacheSizeEnabled = true; 96 | 97 | // Seed cache with some data 98 | cacheManager.UpdateCache(typeof(int), typeof(double), isConvertable: true, conversionAttempt: new CastAttempt()); 99 | cacheManager.UpdateCache(typeof(int), typeof(string), isConvertable: true, conversionAttempt: new CastAttempt()); 100 | cacheManager.UpdateCache(typeof(int), typeof(long), isConvertable: true, conversionAttempt: new CastAttempt()); 101 | 102 | // Perform some read actions to weight the data 103 | for (int i = 0; i < 3; i++) 104 | { 105 | cacheManager.TryGetCachedValue(typeof(int), typeof(double)); 106 | cacheManager.TryGetCachedValue(typeof(int), typeof(string)); 107 | } 108 | 109 | // Act: Insert new data 110 | cacheManager.UpdateCache(typeof(int), typeof(decimal), isConvertable: true, conversionAttempt: new CastAttempt()); 111 | 112 | // Assert: Check what data has to leave the buffer 113 | var cacheResultIntDecimal = cacheManager.TryGetCachedValue(typeof(int), typeof(decimal)); 114 | cacheResultIntDecimal.IsCached.Should().BeTrue(); 115 | 116 | var cacheResultIntLong = cacheManager.TryGetCachedValue(typeof(int), typeof(long)); 117 | cacheResultIntLong.IsCached.Should().BeFalse(); 118 | } 119 | 120 | [Fact] 121 | public void ShouldDisableCacheSizeLimit() 122 | { 123 | // Arrange 124 | var cacheManager = new CacheManager(); 125 | cacheManager.IsCacheEnabled = true; 126 | cacheManager.MaxCacheSize = 3; 127 | cacheManager.IsMaxCacheSizeEnabled = false; 128 | 129 | // Seed cache with some data 130 | cacheManager.UpdateCache(typeof(int), typeof(double), isConvertable: true, conversionAttempt: new CastAttempt()); 131 | cacheManager.UpdateCache(typeof(int), typeof(string), isConvertable: true, conversionAttempt: new CastAttempt()); 132 | cacheManager.UpdateCache(typeof(int), typeof(long), isConvertable: true, conversionAttempt: new CastAttempt()); 133 | 134 | // Act: Insert new data 135 | cacheManager.UpdateCache(typeof(int), typeof(decimal), isConvertable: true, conversionAttempt: new CastAttempt()); 136 | 137 | // Assert: Check what data has to leave the buffer 138 | var cacheResultIntDecimal = cacheManager.TryGetCachedValue(typeof(int), typeof(decimal)); 139 | cacheResultIntDecimal.IsCached.Should().BeTrue(); 140 | 141 | var cacheResultIntLong = cacheManager.TryGetCachedValue(typeof(int), typeof(long)); 142 | cacheResultIntLong.IsCached.Should().BeTrue(); 143 | } 144 | 145 | 146 | [Fact] 147 | public void ShouldReduceCacheSizeWhenMaxCacheSizeIsEnabled() 148 | { 149 | // Arrange 150 | var cacheManager = new CacheManager(); 151 | cacheManager.IsCacheEnabled = true; 152 | cacheManager.MaxCacheSize = 3; 153 | cacheManager.IsMaxCacheSizeEnabled = false; 154 | 155 | cacheManager.UpdateCache(typeof(int), typeof(double), isConvertable: true, conversionAttempt: new CastAttempt()); 156 | cacheManager.UpdateCache(typeof(int), typeof(string), isConvertable: true, conversionAttempt: new CastAttempt()); 157 | cacheManager.UpdateCache(typeof(int), typeof(long), isConvertable: true, conversionAttempt: new CastAttempt()); 158 | cacheManager.UpdateCache(typeof(int), typeof(decimal), isConvertable: true, conversionAttempt: new CastAttempt()); 159 | 160 | // Perform some read actions to weight the data 161 | for (int i = 0; i < 3; i++) 162 | { 163 | cacheManager.TryGetCachedValue(typeof(int), typeof(double)); 164 | cacheManager.TryGetCachedValue(typeof(int), typeof(string)); 165 | } 166 | cacheManager.TryGetCachedValue(typeof(int), typeof(decimal)); 167 | 168 | // Act 169 | cacheManager.IsMaxCacheSizeEnabled = true; 170 | 171 | // Assert 172 | var cacheResultIntDecimal = cacheManager.TryGetCachedValue(typeof(int), typeof(decimal)); 173 | cacheResultIntDecimal.IsCached.Should().BeTrue(); 174 | 175 | var cacheResultIntLong = cacheManager.TryGetCachedValue(typeof(int), typeof(long)); 176 | cacheResultIntLong.IsCached.Should().BeFalse(); 177 | } 178 | } 179 | } -------------------------------------------------------------------------------- /TypeConverter.Tests/ConverterRegistryExtensionsTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | using FluentAssertions; 4 | 5 | using TypeConverter.Converters; 6 | using TypeConverter.Extensions; 7 | 8 | using Xunit; 9 | 10 | namespace TypeConverter.Tests 11 | { 12 | public class ConverterRegistryExtensionsTests 13 | { 14 | [Fact] 15 | public void ShouldRegisterMultipleConvertables() 16 | { 17 | // Arrange 18 | const string InputUriString = "http://www.superdev.ch/"; 19 | const string InputBoolString = "True"; 20 | IConverterRegistry converterRegistry = new ConverterRegistry(); 21 | IConvertable[] converters = { new StringToUriConverter(), new StringToBoolConverter() }; 22 | converterRegistry.RegisterConverters(converters); 23 | 24 | // Act 25 | var outputUri = converterRegistry.Convert(InputUriString); 26 | var outputUriString = converterRegistry.Convert(outputUri); 27 | 28 | var outputBool = converterRegistry.Convert(InputBoolString); 29 | var outputBoolString = converterRegistry.Convert(outputBool); 30 | 31 | 32 | // Assert 33 | outputUriString.Should().Be(InputUriString); 34 | outputBoolString.Should().Be(InputBoolString); 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /TypeConverter.Tests/ConverterRegistryTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | using FluentAssertions; 5 | using TypeConverter.Converters; 6 | using TypeConverter.Exceptions; 7 | using TypeConverter.Tests.Stubs; 8 | using TypeConverter.Tests.Utils; 9 | 10 | using Xunit; 11 | using Xunit.Abstractions; 12 | 13 | namespace TypeConverter.Tests 14 | { 15 | public class ConverterRegistryTests 16 | { 17 | private readonly ITestOutputHelper testOutputHelper; 18 | 19 | public ConverterRegistryTests(ITestOutputHelper testOutputHelper) 20 | { 21 | this.testOutputHelper = testOutputHelper; 22 | } 23 | 24 | #region IConvertable Tests 25 | 26 | [Fact] 27 | public void ShouldThrowConversionNotSupportedExceptionWhenTryingToConvertWithoutValidRegistration() 28 | { 29 | // Arrange 30 | const string InputString = "http://www.superdev.ch/"; 31 | IConverterRegistry converterRegistry = new ConverterRegistry(); 32 | 33 | // Act 34 | Action action = () => converterRegistry.Convert(typeof(string), typeof(Uri), InputString); 35 | 36 | // Assert 37 | Assert.Throws(action); 38 | } 39 | 40 | [Fact] 41 | public void ShouldThrowConversionNotSupportedExceptionWhenTryingToConvertGenericWithoutValidRegistration() 42 | { 43 | // Arrange 44 | const string InputString = "http://www.superdev.ch/"; 45 | IConverterRegistry converterRegistry = new ConverterRegistry(); 46 | 47 | // Act 48 | Action action = () => converterRegistry.Convert(InputString); 49 | 50 | // Assert 51 | Assert.Throws(action); 52 | } 53 | 54 | [Fact] 55 | public void ShouldThrowConversionNotSupportedExceptionWhenWrongConversionWayIsConfigured() 56 | { 57 | // Arrange 58 | const string InputString = "http://www.superdev.ch/"; 59 | IConverterRegistry converterRegistry = new ConverterRegistry(); 60 | converterRegistry.RegisterConverter(() => new StringToUriConverter()); 61 | 62 | // Act 63 | Action action = () => converterRegistry.Convert(typeof(string), typeof(Uri), InputString); 64 | 65 | // Assert 66 | Assert.Throws(action); 67 | } 68 | 69 | [Fact] 70 | public void ShouldRegisterConverterImplicitly() 71 | { 72 | // Arrange 73 | const string InputString = "http://www.superdev.ch/"; 74 | var stringToUriConverter = new StringToUriConverter(); 75 | IConverterRegistry converterRegistry = new ConverterRegistry(); 76 | converterRegistry.RegisterConverter(stringToUriConverter); 77 | 78 | // Act 79 | var convertedObject = converterRegistry.Convert(InputString); 80 | var outputString = converterRegistry.Convert(convertedObject); 81 | 82 | // Assert 83 | convertedObject.Should().NotBeNull(); 84 | convertedObject.Should().BeOfType(); 85 | convertedObject.As().AbsoluteUri.Should().Be(InputString); 86 | 87 | outputString.Should().NotBeNullOrEmpty(); 88 | outputString.Should().Be(InputString); 89 | } 90 | 91 | [Fact] 92 | public void ShouldConvertUsingConverterType() 93 | { 94 | // Arrange 95 | const string InputString = "http://www.superdev.ch/"; 96 | Type converterType = typeof(StringToUriConverter); 97 | IConverterRegistry converterRegistry = new ConverterRegistry(); 98 | converterRegistry.RegisterConverter(converterType); 99 | converterRegistry.RegisterConverter(converterType); 100 | 101 | // Act 102 | var convertedObject = converterRegistry.Convert(InputString); 103 | var outputString = converterRegistry.Convert(convertedObject); 104 | 105 | // Assert 106 | convertedObject.Should().NotBeNull(); 107 | convertedObject.Should().BeOfType(); 108 | convertedObject.As().AbsoluteUri.Should().Be(InputString); 109 | 110 | outputString.Should().NotBeNullOrEmpty(); 111 | outputString.Should().Be(InputString); 112 | } 113 | 114 | [Fact] 115 | public void ShouldConvertIfSourceTypeEqualsTargetType() 116 | { 117 | // Arrange 118 | var inputUri = new Uri("http://www.superdev.ch/"); 119 | IConverterRegistry converterRegistry = new ConverterRegistry(); 120 | 121 | // Act 122 | var outputUri = (Uri)converterRegistry.Convert(typeof(Uri), typeof(Uri), inputUri); 123 | 124 | // Assert 125 | outputUri.Should().NotBeNull(); 126 | outputUri.Should().Be(inputUri); 127 | } 128 | 129 | [Fact] 130 | public void ShouldConvertUsingGenericSourceTypeAndNongenericTargetType() 131 | { 132 | // Arrange 133 | const string InputString = "http://www.superdev.ch/"; 134 | Type converterType = typeof(StringToUriConverter); 135 | IConverterRegistry converterRegistry = new ConverterRegistry(); 136 | converterRegistry.RegisterConverter(converterType); 137 | converterRegistry.RegisterConverter(converterType); 138 | 139 | // Act 140 | var convertedObject = (Uri)converterRegistry.Convert(typeof(Uri), InputString); 141 | var outputString = converterRegistry.Convert(typeof(string), convertedObject); 142 | 143 | // Assert 144 | convertedObject.Should().NotBeNull(); 145 | convertedObject.Should().BeOfType(); 146 | convertedObject.As().AbsoluteUri.Should().Be(InputString); 147 | 148 | outputString.Should().NotBeNull(); 149 | outputString.Should().Be(InputString); 150 | } 151 | 152 | [Fact] 153 | public void ShouldConvertUsingGenericTargetTypeAndObjectSourceType() 154 | { 155 | // Arrange 156 | const string InputString = "http://www.superdev.ch/"; 157 | Type converterType = typeof(StringToUriConverter); 158 | IConverterRegistry converterRegistry = new ConverterRegistry(); 159 | converterRegistry.RegisterConverter(converterType); 160 | converterRegistry.RegisterConverter(converterType); 161 | 162 | // Act 163 | var convertedObject = (Uri)converterRegistry.Convert(InputString); 164 | var outputString = converterRegistry.Convert(convertedObject); 165 | 166 | // Assert 167 | convertedObject.Should().NotBeNull(); 168 | convertedObject.Should().BeOfType(); 169 | convertedObject.As().AbsoluteUri.Should().Be(InputString); 170 | 171 | outputString.Should().NotBeNull(); 172 | outputString.Should().Be(InputString); 173 | } 174 | 175 | [Fact] 176 | public void ShouldReturnDefaultValueWhenTryConvertToReferenceTypeFails() 177 | { 178 | // Arrange 179 | const string InputString = "http://www.superdev.ch/"; 180 | IConverterRegistry converterRegistry = new ConverterRegistry(); 181 | 182 | // Act 183 | var convertedObject = converterRegistry.TryConvert(InputString, null); 184 | 185 | // Assert 186 | convertedObject.Should().BeNull(); 187 | } 188 | 189 | [Fact] 190 | public void ShouldReturnDefaultValueWhenTryConvertToValueTypeFails() 191 | { 192 | // Arrange 193 | TestStruct1 testStruct1 = new TestStruct1 { TestString = Guid.NewGuid().ToString() }; 194 | IConverterRegistry converterRegistry = new ConverterRegistry(); 195 | 196 | // Act 197 | var convertedObject = converterRegistry.TryConvert(testStruct1, default(Guid)); 198 | 199 | // Assert 200 | convertedObject.Should().Be(Guid.Empty); 201 | } 202 | 203 | [Fact] 204 | public void ShouldTryConvertEnumImplicitlyWithGenericMethod() 205 | { 206 | // Arrange 207 | object sourceObject = MyEnum.TestValue; 208 | const MyEnum DefaultValue = default(MyEnum); 209 | IConverterRegistry converterRegistry = new ConverterRegistry(); 210 | 211 | // Act 212 | MyEnum convertedObject = converterRegistry.TryConvert(sourceObject, DefaultValue); 213 | 214 | // Assert 215 | convertedObject.Should().Be(sourceObject); 216 | } 217 | 218 | [Fact] 219 | public void ShouldTryConvertEnumImplicitlyWithNonGenericMethod() 220 | { 221 | // Arrange 222 | object sourceObject = MyEnum.TestValue; 223 | const MyEnum DefaultValue = default(MyEnum); 224 | IConverterRegistry converterRegistry = new ConverterRegistry(); 225 | 226 | // Act 227 | object convertedObject = converterRegistry.TryConvert(typeof(object), typeof(MyEnum), sourceObject, DefaultValue); 228 | 229 | // Assert 230 | convertedObject.Should().Be(sourceObject); 231 | } 232 | 233 | #endregion 234 | 235 | #region Implicit and explicit cast tests 236 | 237 | [Fact] 238 | public void ShouldConvertIfSourceTypeIsEqualToTargetType() 239 | { 240 | // Arrange 241 | const string InputString = "999"; 242 | IConverterRegistry converterRegistry = new ConverterRegistry(); 243 | 244 | // Act 245 | var convertedObject = (string)converterRegistry.Convert(typeof(string), typeof(string), InputString); 246 | 247 | // Assert 248 | convertedObject.Should().Be(InputString); 249 | } 250 | 251 | [Fact] 252 | public void ShouldConvertIfTargetTypeIsAssignableFromSourceType() 253 | { 254 | // Arrange 255 | List stringList = new List { "a", "b", "c" }; 256 | IConverterRegistry converterRegistry = new ConverterRegistry(); 257 | 258 | // Act 259 | var convertedList = (IEnumerable)converterRegistry.Convert(typeof(IEnumerable), stringList); 260 | 261 | // Assert 262 | convertedList.Should().BeEquivalentTo(stringList); 263 | } 264 | 265 | [Fact] 266 | public void ShouldConvertEnumerableToArray() 267 | { 268 | // Arrange 269 | string[] stringArray = { "a", "b", "c" }; 270 | IConverterRegistry converterRegistry = new ConverterRegistry(); 271 | 272 | // Act 273 | var convertedList = (IEnumerable)converterRegistry.Convert(typeof(IEnumerable), stringArray); 274 | 275 | // Assert 276 | convertedList.Should().BeEquivalentTo(stringArray); 277 | } 278 | 279 | [Fact] 280 | public void ShouldThrowConversionNotSupportedExceptionWhenTryingToConvertArrayToEnumerable() 281 | { 282 | // Arrange 283 | List stringList = new List { "a", "b", "c" }; 284 | IConverterRegistry converterRegistry = new ConverterRegistry(); 285 | 286 | // Act 287 | Action action = () => converterRegistry.Convert(typeof(string[]), stringList); 288 | 289 | // Assert 290 | Assert.Throws(action); 291 | } 292 | 293 | [Fact] 294 | public void ShouldConvertNullableTypeToValueType() 295 | { 296 | // Arrange 297 | bool? nullableValue = true; 298 | IConverterRegistry converterRegistry = new ConverterRegistry(); 299 | 300 | // Act 301 | var valueType = converterRegistry.Convert(nullableValue); 302 | 303 | // Assert 304 | valueType.Should().Be(nullableValue.Value); 305 | } 306 | 307 | [Fact] 308 | public void ShouldConvertValueTypeToNullableType() 309 | { 310 | // Arrange 311 | const bool ValueType = true; 312 | IConverterRegistry converterRegistry = new ConverterRegistry(); 313 | 314 | // Act 315 | var nullableValue = converterRegistry.Convert(ValueType); 316 | 317 | // Assert 318 | nullableValue.Should().Be(ValueType); 319 | } 320 | 321 | [Fact] 322 | public void ShouldConvertDoubleToIntegerExplicitly() 323 | { 324 | // Arrange 325 | const double DoubleValue = 999.99d; 326 | IConverterRegistry converterRegistry = new ConverterRegistry(); 327 | 328 | // Act 329 | var convertedValue = converterRegistry.Convert(DoubleValue); 330 | 331 | // Assert 332 | convertedValue.Should().Be((int)DoubleValue); 333 | } 334 | 335 | [Fact] 336 | public void ShouldConvertIntegerToDoubleExplicitly() 337 | { 338 | // Arrange 339 | //const double DoubleValue = 0.0d; 340 | int IntegerValue = 999; 341 | IConverterRegistry converterRegistry = new ConverterRegistry(); 342 | 343 | 344 | // Act 345 | var convertedValue = converterRegistry.TryConvert((object)(int)999, (int)0); 346 | var convertedValue2 = converterRegistry.TryConvert((object)(int)999, (double)0); 347 | 348 | var convertedValue3 = converterRegistry.TryConvert((object)(int)999, (int)0); 349 | var convertedValue4 = converterRegistry.TryConvert((object)(int)999, (double)0); 350 | 351 | // Assert 352 | convertedValue.Should().Be(IntegerValue); 353 | } 354 | 355 | [Fact] 356 | public void ShouldConvertULongToDecimalImplicitly() 357 | { 358 | // Arrange 359 | const ulong UlongValue = 999UL; 360 | IConverterRegistry converterRegistry = new ConverterRegistry(); 361 | 362 | // Act 363 | var convertedValue = converterRegistry.Convert(UlongValue); 364 | 365 | // Assert 366 | convertedValue.Should().Be(Convert.ToDecimal(UlongValue)); 367 | } 368 | 369 | [Fact] 370 | public void ShouldConvertFromOpenGenericTypeToGenericType() 371 | { 372 | // Arrange 373 | IGenericOperators inputValue = new Operators(); 374 | IConverterRegistry converterRegistry = new ConverterRegistry(); 375 | 376 | // Act 377 | var convertedValue = converterRegistry.Convert(typeof(IGenericOperators<>), typeof(IGenericOperators), inputValue); 378 | 379 | // Assert 380 | convertedValue.Should().Be(inputValue); 381 | } 382 | 383 | [Fact] 384 | public void ShouldThrowConversionNotSupportedExceptionWhenTryingToConvertToOpenGenericType() 385 | { 386 | // Arrange 387 | IGenericOperators value = new Operators(); 388 | IConverterRegistry converterRegistry = new ConverterRegistry(); 389 | 390 | // Act 391 | Action action = () => converterRegistry.Convert(typeof(IGenericOperators), typeof(IGenericOperators<>), value); 392 | 393 | // Assert 394 | Assert.Throws(action); 395 | } 396 | 397 | [Fact] 398 | public void ShouldRunAllDefaultCasts() 399 | { 400 | IConverterRegistry converterRegistry = new ConverterRegistry(); 401 | 402 | CastTestRunner.RunTests((testCase) => 403 | { 404 | // Arrange 405 | var value = CastTestRunner.GenerateValueForType(testCase.SourceType); 406 | var generatedTestSuccessful = CastTestRunner.CastValueWithGeneratedCode(value, testCase.SourceType, testCase.TargetType, testCase.CastFlag); 407 | 408 | // Act 409 | var convertedObject = converterRegistry.TryConvert( 410 | sourceType: testCase.SourceType, 411 | targetType: testCase.TargetType, 412 | value: value, 413 | defaultReturnValue: null); 414 | 415 | // Assert 416 | var castResult = new CastResult(convertedObject, testCase.CastFlag); 417 | var isSuccessful = CastTestRunner.AreEqual( 418 | this.testOutputHelper, 419 | testCase.SourceType, 420 | testCase.TargetType, 421 | generatedTestSuccessful, 422 | castResult, 423 | testCase.CastFlag); 424 | 425 | return isSuccessful; 426 | }); 427 | } 428 | 429 | #endregion 430 | 431 | #region Enum Parse Tests 432 | 433 | [Fact] 434 | public void ShouldConvertEnumsImplicitly() 435 | { 436 | // Arrange 437 | string inputString = MyEnum.TestValue.ToString(); 438 | IConverterRegistry converterRegistry = new ConverterRegistry(); 439 | 440 | // Act 441 | var convertedObject = (MyEnum)converterRegistry.Convert(typeof(MyEnum), inputString); 442 | var outputString = converterRegistry.Convert(typeof(string), convertedObject); 443 | 444 | // Assert 445 | convertedObject.Should().NotBeNull(); 446 | convertedObject.Should().BeOfType(); 447 | convertedObject.Should().Be(MyEnum.TestValue); 448 | 449 | outputString.Should().NotBeNull(); 450 | outputString.Should().Be(inputString); 451 | } 452 | 453 | [Fact] 454 | public void ShouldConvertEnumsImplicitlyWithGenerics() 455 | { 456 | // Arrange 457 | string inputString = MyEnum.TestValue.ToString(); 458 | IConverterRegistry converterRegistry = new ConverterRegistry(); 459 | 460 | // Act 461 | var convertedObject = converterRegistry.Convert(inputString); 462 | var outputString = converterRegistry.Convert(typeof(string), convertedObject); 463 | 464 | // Assert 465 | convertedObject.Should().NotBeNull(); 466 | convertedObject.Should().BeOfType(); 467 | convertedObject.Should().Be(MyEnum.TestValue); 468 | 469 | outputString.Should().NotBeNull(); 470 | outputString.Should().Be(inputString); 471 | } 472 | 473 | [Fact] 474 | public void ShouldConvertEnumsExplicitly() 475 | { 476 | // Arrange 477 | string inputString = MyEnum.TestValue.ToString(); 478 | IConverterRegistry converterRegistry = new ConverterRegistry(); 479 | converterRegistry.RegisterConverter(() => new MyEnumConverter()); 480 | converterRegistry.RegisterConverter(() => new MyEnumConverter()); 481 | 482 | // Act 483 | var convertedObject = (MyEnum)converterRegistry.Convert(inputString); 484 | var outputString = converterRegistry.Convert(convertedObject); 485 | 486 | // Assert 487 | convertedObject.Should().NotBeNull(); 488 | convertedObject.Should().BeOfType(); 489 | convertedObject.Should().Be(MyEnum.TestValue); 490 | 491 | outputString.Should().NotBeNull(); 492 | outputString.Should().Be(inputString); 493 | } 494 | 495 | #endregion 496 | 497 | #region ChangeType Tests 498 | 499 | [Fact] 500 | public void ShouldConvertUsingChangeType() 501 | { 502 | // Arrange 503 | bool? nullableBool = true; 504 | string valueTypeString = nullableBool.ToString(); 505 | IConverterRegistry converterRegistry = new ConverterRegistry(); 506 | 507 | // Act 508 | var nullableValue = converterRegistry.Convert(valueTypeString); 509 | 510 | // Assert 511 | nullableValue.Should().Be(nullableBool.Value); 512 | } 513 | 514 | [Fact] 515 | public void ShouldConvertUsingChangeTypeMethodFromStringToInt() 516 | { 517 | // Arrange 518 | const string InputString = "999"; 519 | IConverterRegistry converterRegistry = new ConverterRegistry(); 520 | 521 | // Act 522 | var convertedObject = (int)converterRegistry.Convert(typeof(int), InputString); 523 | var outputString = converterRegistry.Convert(typeof(string), convertedObject); 524 | 525 | // Assert 526 | convertedObject.Should().Be(999); 527 | 528 | outputString.Should().NotBeNull(); 529 | outputString.Should().Be(InputString); 530 | } 531 | 532 | [Fact] 533 | public void ShouldConvertUsingChangeTypeMethodFromStringToBool() 534 | { 535 | // Arrange 536 | const string InputString = "True"; 537 | IConverterRegistry converterRegistry = new ConverterRegistry(); 538 | 539 | // Act 540 | var convertedObject = (bool)converterRegistry.Convert(typeof(bool), InputString); 541 | var outputString = converterRegistry.Convert(typeof(string), convertedObject); 542 | 543 | // Assert 544 | convertedObject.Should().BeTrue(); 545 | 546 | outputString.Should().NotBeNull(); 547 | outputString.Should().Be(InputString); 548 | } 549 | #endregion 550 | 551 | #region String Parse Tests 552 | [Fact] 553 | public void ShouldConvertUsingStringParse() 554 | { 555 | // Arrange 556 | const string InputString = "http://www.thomasgalliker.ch/"; 557 | Uri inputUri = new Uri(InputString); 558 | 559 | IConverterRegistry converterRegistry = new ConverterRegistry(); 560 | 561 | // Act 562 | var uriAsString = converterRegistry.Convert(inputUri); 563 | 564 | // Assert 565 | uriAsString.Should().Be(InputString); 566 | } 567 | #endregion 568 | 569 | #region Mapping Tests 570 | [Fact] 571 | public void ShouldConvertUsingRegisterMapping() 572 | { 573 | // Arrange 574 | var model = new ModelA { Name = "Thomas", Age = 30 }; 575 | 576 | IConverterRegistry converterRegistry = new ConverterRegistry(); 577 | converterRegistry.RegisterMapping(src => src.Name, dest => dest.Name); 578 | converterRegistry.RegisterMapping(src => src.Age, dest => dest.Age); 579 | 580 | // Act 581 | var viewModelA = converterRegistry.Convert(model); 582 | 583 | // Assert 584 | viewModelA.Name.Should().Be("Thomas"); 585 | viewModelA.Age.Should().Be(30); 586 | } 587 | #endregion 588 | 589 | #region General Tests 590 | [Fact] 591 | public void ShouldResetRegistrations() 592 | { 593 | // Arrange 594 | const string InputString = "http://www.thomasgalliker.ch"; 595 | int numberOfConvertCalls = 0; 596 | IConverterRegistry converterRegistry = new ConverterRegistry(); 597 | var converter = new TestConverter(() => { numberOfConvertCalls++; }); 598 | converterRegistry.RegisterConverter(() => converter); 599 | 600 | // Act 601 | var convertedInputStringBeforeReset = converterRegistry.TryConvert(InputString, null); 602 | 603 | converterRegistry.Reset(); 604 | 605 | var convertedInputStringAfterReset = converterRegistry.TryConvert(InputString, null); 606 | 607 | // Assert 608 | numberOfConvertCalls.Should().Be(1); 609 | convertedInputStringBeforeReset.Should().Be(InputString); 610 | convertedInputStringAfterReset.Should().BeNull(); 611 | } 612 | 613 | private class TestConverter : IConvertable 614 | { 615 | private readonly Action convert; 616 | 617 | public TestConverter(Action convert) 618 | { 619 | this.convert = convert; 620 | } 621 | 622 | public Uri Convert(string value) 623 | { 624 | this.convert(); 625 | return new Uri(value); 626 | } 627 | } 628 | #endregion 629 | } 630 | 631 | public class ViewModelA 632 | { 633 | public string Name { get; set; } 634 | 635 | public int Age { get; set; } 636 | } 637 | 638 | public class ModelA 639 | { 640 | public string Name { get; set; } 641 | 642 | public int Age { get; set; } 643 | } 644 | } -------------------------------------------------------------------------------- /TypeConverter.Tests/Converters/StringToBoolConverterTests.cs: -------------------------------------------------------------------------------- 1 | using FluentAssertions; 2 | 3 | using TypeConverter.Converters; 4 | 5 | using Xunit; 6 | 7 | namespace TypeConverter.Tests.Converters 8 | { 9 | public class StringToBoolConverterTests 10 | { 11 | [Fact] 12 | public void ShouldConvertBothWays() 13 | { 14 | // Arrange 15 | const string InputString = "True"; 16 | IConverterRegistry converterRegistry = new ConverterRegistry(); 17 | converterRegistry.RegisterConverter(() => new StringToBoolConverter()); 18 | converterRegistry.RegisterConverter(() => new StringToBoolConverter()); 19 | 20 | // Act 21 | var convertedObject = converterRegistry.Convert(InputString); 22 | var outputString = converterRegistry.Convert(convertedObject); 23 | 24 | // Assert 25 | convertedObject.Should().BeTrue(); 26 | 27 | outputString.Should().NotBeNullOrEmpty(); 28 | outputString.Should().Be(InputString); 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /TypeConverter.Tests/Converters/StringToDateTimeConverterTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | using FluentAssertions; 4 | 5 | using TypeConverter.Converters; 6 | 7 | using Xunit; 8 | 9 | namespace TypeConverter.Tests.Converters 10 | { 11 | public class StringToDateTimeConverterTests 12 | { 13 | [Fact] 14 | public void ShouldConvertDateTimeToString_Universal() 15 | { 16 | // Arrange 17 | DateTime intputDateTime = new DateTime(1999, 12, 31, 23, 59, 59, DateTimeKind.Utc); 18 | IConverterRegistry converterRegistry = new ConverterRegistry(); 19 | converterRegistry.RegisterConverter(() => new StringToDateTimeConverter()); 20 | 21 | // Act 22 | var outputString = converterRegistry.Convert(intputDateTime); 23 | 24 | // Assert 25 | outputString.Should().Be("1999-12-31T23:59:59.0000000Z"); 26 | } 27 | 28 | [Fact] 29 | public void ShouldConvertStringToDateTime_Universal() 30 | { 31 | // Arrange 32 | const string InputString = "1999-12-31T23:59:59.0000000Z"; 33 | IConverterRegistry converterRegistry = new ConverterRegistry(); 34 | converterRegistry.RegisterConverter(() => new StringToDateTimeConverter()); 35 | 36 | // Act 37 | var outputDateTime = converterRegistry.Convert(InputString); 38 | 39 | // Assert 40 | outputDateTime.Should().Be(new DateTime(1999, 12, 31, 23, 59, 59, DateTimeKind.Utc)); 41 | outputDateTime.Kind.Should().Be(DateTimeKind.Utc); 42 | } 43 | 44 | [Fact] 45 | public void ShouldConvertDateTimeToString_Local() 46 | { 47 | // Arrange 48 | DateTime intputDateTime = new DateTime(1999, 12, 31, 23, 59, 59, DateTimeKind.Local); 49 | IConverterRegistry converterRegistry = new ConverterRegistry(); 50 | converterRegistry.RegisterConverter(() => new StringToDateTimeConverter()); 51 | 52 | // Act 53 | var outputString = converterRegistry.Convert(intputDateTime); 54 | 55 | // Assert 56 | outputString.Should().Be("1999-12-31T23:59:59.0000000+01:00"); 57 | } 58 | 59 | [Fact] 60 | public void ShouldConvertStringToDateTime_Local() 61 | { 62 | // Arrange 63 | const string InputString = "1999-12-31T23:59:59.0000000+01:00"; 64 | IConverterRegistry converterRegistry = new ConverterRegistry(); 65 | converterRegistry.RegisterConverter(() => new StringToDateTimeConverter()); 66 | 67 | // Act 68 | var outputDateTime = converterRegistry.Convert(InputString); 69 | 70 | // Assert 71 | outputDateTime.Should().Be(new DateTime(1999, 12, 31, 23, 59, 59, DateTimeKind.Local)); 72 | outputDateTime.Kind.Should().Be(DateTimeKind.Local); 73 | } 74 | } 75 | } -------------------------------------------------------------------------------- /TypeConverter.Tests/Converters/StringToDateTimeOffsetConverterTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | using FluentAssertions; 4 | 5 | using TypeConverter.Converters; 6 | 7 | using Xunit; 8 | 9 | namespace TypeConverter.Tests.Converters 10 | { 11 | public class StringToDateTimeOffsetConverterTests 12 | { 13 | [Fact] 14 | public void ShouldConvertDateTimeToString_Universal() 15 | { 16 | // Arrange 17 | DateTimeOffset intputDateTime = new DateTimeOffset(new DateTime(1999, 12, 31, 23, 59, 59, DateTimeKind.Utc)); 18 | IConverterRegistry converterRegistry = new ConverterRegistry(); 19 | converterRegistry.RegisterConverter(() => new StringToDateTimeOffsetConverter()); 20 | 21 | // Act 22 | var outputString = converterRegistry.Convert(intputDateTime); 23 | 24 | // Assert 25 | outputString.Should().Be("1999-12-31T23:59:59.0000000+00:00"); 26 | } 27 | 28 | [Fact] 29 | public void ShouldConvertStringToDateTime_Universal() 30 | { 31 | // Arrange 32 | const string InputString = "1999-12-31T23:59:59.0000000+00:00"; 33 | IConverterRegistry converterRegistry = new ConverterRegistry(); 34 | converterRegistry.RegisterConverter(() => new StringToDateTimeOffsetConverter()); 35 | 36 | // Act 37 | var outputDateTime = converterRegistry.Convert(InputString); 38 | 39 | // Assert 40 | outputDateTime.Should().Be(new DateTimeOffset(new DateTime(1999, 12, 31, 23, 59, 59, DateTimeKind.Utc))); 41 | } 42 | 43 | [Fact] 44 | public void ShouldConvertDateTimeToString_Local() 45 | { 46 | // Arrange 47 | DateTimeOffset intputDateTime = new DateTimeOffset(new DateTime(1999, 12, 31, 23, 59, 59), new TimeSpan(-7, 0, 0)); 48 | IConverterRegistry converterRegistry = new ConverterRegistry(); 49 | converterRegistry.RegisterConverter(() => new StringToDateTimeOffsetConverter()); 50 | 51 | // Act 52 | var outputString = converterRegistry.Convert(intputDateTime); 53 | 54 | // Assert 55 | outputString.Should().Be("1999-12-31T23:59:59.0000000-07:00"); 56 | } 57 | 58 | [Fact] 59 | public void ShouldConvertStringToDateTime_Local() 60 | { 61 | // Arrange 62 | const string InputString = "1999-12-31T23:59:59.0000000-07:00"; 63 | IConverterRegistry converterRegistry = new ConverterRegistry(); 64 | converterRegistry.RegisterConverter(() => new StringToDateTimeOffsetConverter()); 65 | 66 | // Act 67 | var outputDateTime = converterRegistry.Convert(InputString); 68 | 69 | // Assert 70 | outputDateTime.Should().Be(new DateTimeOffset(new DateTime(1999, 12, 31, 23, 59, 59), new TimeSpan(-7, 0, 0))); 71 | } 72 | } 73 | } -------------------------------------------------------------------------------- /TypeConverter.Tests/Converters/StringToDecimalConverterTests.cs: -------------------------------------------------------------------------------- 1 | using FluentAssertions; 2 | 3 | using TypeConverter.Converters; 4 | 5 | using Xunit; 6 | 7 | namespace TypeConverter.Tests.Converters 8 | { 9 | public class StringToDecimalConverterTests 10 | { 11 | [Fact] 12 | public void ShouldConvertDecimalMaxValueToString() 13 | { 14 | // Arrange 15 | decimal inputDecimal = decimal.MaxValue; 16 | IConverterRegistry converterRegistry = new ConverterRegistry(); 17 | converterRegistry.RegisterConverter(() => new StringToDecimalConverter()); 18 | 19 | // Act 20 | var outputString = converterRegistry.Convert(inputDecimal); 21 | 22 | // Assert 23 | outputString.Should().Be("79228162514264337593543950335"); 24 | } 25 | 26 | [Fact] 27 | public void ShouldConvertDecimalMinValueToString() 28 | { 29 | // Arrange 30 | decimal inputDecimal = decimal.MinValue; 31 | IConverterRegistry converterRegistry = new ConverterRegistry(); 32 | converterRegistry.RegisterConverter(() => new StringToDecimalConverter()); 33 | 34 | // Act 35 | var outputString = converterRegistry.Convert(inputDecimal); 36 | 37 | // Assert 38 | outputString.Should().Be("-79228162514264337593543950335"); 39 | } 40 | 41 | [Fact] 42 | public void ShouldConvertStringToDecimalMaxValue() 43 | { 44 | // Arrange 45 | const string InputString = "79228162514264337593543950335"; 46 | IConverterRegistry converterRegistry = new ConverterRegistry(); 47 | converterRegistry.RegisterConverter(() => new StringToDecimalConverter()); 48 | 49 | // Act 50 | var outputDecimal = converterRegistry.Convert(InputString); 51 | 52 | // Assert 53 | outputDecimal.Should().Be(decimal.MaxValue); 54 | } 55 | 56 | [Fact] 57 | public void ShouldConvertStringToDecimalMinValue() 58 | { 59 | // Arrange 60 | const string InputString = "-79228162514264337593543950335"; 61 | IConverterRegistry converterRegistry = new ConverterRegistry(); 62 | converterRegistry.RegisterConverter(() => new StringToDecimalConverter()); 63 | 64 | // Act 65 | var outputDecimal = converterRegistry.Convert(InputString); 66 | 67 | // Assert 68 | outputDecimal.Should().Be(decimal.MinValue); 69 | } 70 | } 71 | } -------------------------------------------------------------------------------- /TypeConverter.Tests/Converters/StringToDoubleConverterTests.cs: -------------------------------------------------------------------------------- 1 | using FluentAssertions; 2 | 3 | using TypeConverter.Converters; 4 | 5 | using Xunit; 6 | 7 | namespace TypeConverter.Tests.Converters 8 | { 9 | public class StringToDoubleConverterTests 10 | { 11 | [Fact] 12 | public void ShouldConvertDoubleMaxValueToString() 13 | { 14 | // Arrange 15 | double inputDouble = double.MaxValue; 16 | IConverterRegistry converterRegistry = new ConverterRegistry(); 17 | converterRegistry.RegisterConverter(() => new StringToDoubleConverter()); 18 | 19 | // Act 20 | var outputString = converterRegistry.Convert(inputDouble); 21 | 22 | // Assert 23 | outputString.Should().Be("1.7976931348623157E+308"); 24 | } 25 | 26 | [Fact] 27 | public void ShouldConvertDoubleMinValueToString() 28 | { 29 | // Arrange 30 | double inputDouble = double.MinValue; 31 | IConverterRegistry converterRegistry = new ConverterRegistry(); 32 | converterRegistry.RegisterConverter(() => new StringToDoubleConverter()); 33 | 34 | // Act 35 | var outputString = converterRegistry.Convert(inputDouble); 36 | 37 | // Assert 38 | outputString.Should().Be("-1.7976931348623157E+308"); 39 | } 40 | 41 | [Fact] 42 | public void ShouldConvertStringToDoubleMaxValue() 43 | { 44 | // Arrange 45 | const string InputString = "1.7976931348623157E+308"; 46 | IConverterRegistry converterRegistry = new ConverterRegistry(); 47 | converterRegistry.RegisterConverter(() => new StringToDoubleConverter()); 48 | 49 | // Act 50 | var outputDouble = converterRegistry.Convert(InputString); 51 | 52 | // Assert 53 | outputDouble.Should().Be(double.MaxValue); 54 | } 55 | 56 | [Fact] 57 | public void ShouldConvertStringToDoubleMinValue() 58 | { 59 | // Arrange 60 | const string InputString = "-1.7976931348623157E+308"; 61 | IConverterRegistry converterRegistry = new ConverterRegistry(); 62 | converterRegistry.RegisterConverter(() => new StringToDoubleConverter()); 63 | 64 | // Act 65 | var outputDouble = converterRegistry.Convert(InputString); 66 | 67 | // Assert 68 | outputDouble.Should().Be(double.MinValue); 69 | } 70 | } 71 | } -------------------------------------------------------------------------------- /TypeConverter.Tests/Converters/StringToFloatConverterTests.cs: -------------------------------------------------------------------------------- 1 | using FluentAssertions; 2 | 3 | using TypeConverter.Converters; 4 | 5 | using Xunit; 6 | 7 | namespace TypeConverter.Tests.Converters 8 | { 9 | public class StringToFloatConverterTests 10 | { 11 | [Fact] 12 | public void ShouldConvertFloatMaxValueToString() 13 | { 14 | // Arrange 15 | float inputFloat = float.MaxValue; 16 | IConverterRegistry converterRegistry = new ConverterRegistry(); 17 | converterRegistry.RegisterConverter(() => new StringToFloatConverter()); 18 | 19 | // Act 20 | var outputString = converterRegistry.Convert(inputFloat); 21 | 22 | // Assert 23 | outputString.Should().Be("3.40282347E+38"); 24 | } 25 | 26 | [Fact] 27 | public void ShouldConvertFloatMinValueToString() 28 | { 29 | // Arrange 30 | float inputFloat = float.MinValue; 31 | IConverterRegistry converterRegistry = new ConverterRegistry(); 32 | converterRegistry.RegisterConverter(() => new StringToFloatConverter()); 33 | 34 | // Act 35 | var outputString = converterRegistry.Convert(inputFloat); 36 | 37 | // Assert 38 | outputString.Should().Be("-3.40282347E+38"); 39 | } 40 | 41 | [Fact] 42 | public void ShouldConvertStringToFloatMaxValue() 43 | { 44 | // Arrange 45 | const string InputString = "3.40282347E+38"; 46 | IConverterRegistry converterRegistry = new ConverterRegistry(); 47 | converterRegistry.RegisterConverter(() => new StringToFloatConverter()); 48 | 49 | // Act 50 | var outputFloat = converterRegistry.Convert(InputString); 51 | 52 | // Assert 53 | outputFloat.Should().Be(float.MaxValue); 54 | } 55 | 56 | [Fact] 57 | public void ShouldConvertStringToFloatMinValue() 58 | { 59 | // Arrange 60 | const string InputString = "-3.40282347E+38"; 61 | IConverterRegistry converterRegistry = new ConverterRegistry(); 62 | converterRegistry.RegisterConverter(() => new StringToFloatConverter()); 63 | 64 | // Act 65 | var outputFloat = converterRegistry.Convert(InputString); 66 | 67 | // Assert 68 | outputFloat.Should().Be(float.MinValue); 69 | } 70 | } 71 | } -------------------------------------------------------------------------------- /TypeConverter.Tests/Converters/StringToGuidConverterTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | using FluentAssertions; 4 | 5 | using TypeConverter.Converters; 6 | 7 | using Xunit; 8 | 9 | namespace TypeConverter.Tests.Converters 10 | { 11 | public class StringToGuidConverterTests 12 | { 13 | [Fact] 14 | public void ShouldConvertFormatBStringToGuid() 15 | { 16 | // Arrange 17 | const string InputString = "{1E20D9BB-D64C-4449-AC1B-36CB690601ED}"; 18 | IConverterRegistry converterRegistry = new ConverterRegistry(); 19 | converterRegistry.RegisterConverter(() => new StringToGuidConverter()); 20 | 21 | // Act 22 | var outputGuid = converterRegistry.Convert(InputString); 23 | 24 | // Assert 25 | outputGuid.Should().Be(new Guid(InputString)); 26 | } 27 | 28 | [Fact] 29 | public void ShouldConvertFormatNStringToGuid() 30 | { 31 | // Arrange 32 | const string InputString = "4568CA6400E742BAAA41E76916DE7118"; 33 | IConverterRegistry converterRegistry = new ConverterRegistry(); 34 | converterRegistry.RegisterConverter(() => new StringToGuidConverter()); 35 | 36 | // Act 37 | var outputGuid = converterRegistry.Convert(InputString); 38 | 39 | // Assert 40 | outputGuid.Should().Be(new Guid(InputString)); 41 | } 42 | 43 | [Fact] 44 | public void ShouldConvertGuidToBFormatString() 45 | { 46 | // Arrange 47 | var inputGuid = new Guid("83EDDA8A-4538-4BA8-8D40-E82C561CD745"); 48 | IConverterRegistry converterRegistry = new ConverterRegistry(); 49 | converterRegistry.RegisterConverter(() => new StringToGuidConverter()); 50 | 51 | // Act 52 | var outputString = converterRegistry.Convert(inputGuid); 53 | 54 | // Assert 55 | outputString.Should().Be("{83edda8a-4538-4ba8-8d40-e82c561cd745}"); 56 | } 57 | } 58 | } -------------------------------------------------------------------------------- /TypeConverter.Tests/Converters/StringToIntegerConverterTests.cs: -------------------------------------------------------------------------------- 1 | using FluentAssertions; 2 | 3 | using TypeConverter.Converters; 4 | 5 | using Xunit; 6 | 7 | namespace TypeConverter.Tests.Converters 8 | { 9 | public class StringToIntegerConverterTests 10 | { 11 | [Fact] 12 | public void ShouldConvertBothWays() 13 | { 14 | // Arrange 15 | const string InputString = "999"; 16 | IConverterRegistry converterRegistry = new ConverterRegistry(); 17 | converterRegistry.RegisterConverter(() => new StringToIntegerConverter()); 18 | converterRegistry.RegisterConverter(() => new StringToIntegerConverter()); 19 | 20 | // Act 21 | var convertedObject = converterRegistry.Convert(InputString); 22 | var outputString = converterRegistry.Convert(convertedObject); 23 | 24 | // Assert 25 | convertedObject.Should().Be(999); 26 | 27 | outputString.Should().NotBeNullOrEmpty(); 28 | outputString.Should().Be(InputString); 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /TypeConverter.Tests/Converters/StringToUriConverterTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | using FluentAssertions; 4 | 5 | using TypeConverter.Converters; 6 | 7 | using Xunit; 8 | 9 | namespace TypeConverter.Tests.Converters 10 | { 11 | public class StringToUriConverterTests 12 | { 13 | [Fact] 14 | public void ShouldConvertStringToUri() 15 | { 16 | // Arrange 17 | const string InputString = "http://www.superdev.ch/"; 18 | IConverterRegistry converterRegistry = new ConverterRegistry(); 19 | converterRegistry.RegisterConverter(() => new StringToUriConverter()); 20 | converterRegistry.RegisterConverter(() => new StringToUriConverter()); 21 | 22 | // Act 23 | var outputUri = converterRegistry.Convert(InputString); 24 | 25 | // Assert 26 | outputUri.Should().NotBeNull(); 27 | outputUri.AbsoluteUri.Should().Be(InputString); 28 | } 29 | 30 | [Fact] 31 | public void ShouldConvertUriToString() 32 | { 33 | // Arrange 34 | var inputUri = new Uri("http://www.superdev.ch/"); 35 | IConverterRegistry converterRegistry = new ConverterRegistry(); 36 | converterRegistry.RegisterConverter(() => new StringToUriConverter()); 37 | 38 | // Act 39 | var outputString = converterRegistry.Convert(inputUri); 40 | 41 | // Assert 42 | outputString.Should().Be(inputUri.AbsoluteUri); 43 | } 44 | } 45 | } -------------------------------------------------------------------------------- /TypeConverter.Tests/Extensions/TypeExtensionsTests.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using System.Reflection; 3 | 4 | using FluentAssertions; 5 | 6 | using TypeConverter.Extensions; 7 | using TypeConverter.Tests.Stubs; 8 | 9 | using Xunit; 10 | 11 | namespace TypeConverter.Tests.Extensions 12 | { 13 | public class TypeExtensionsTests 14 | { 15 | [Fact] 16 | public void ShouldReturnAllMethodsOfType() 17 | { 18 | // Arrange 19 | var type = typeof(Operators); 20 | 21 | // Act 22 | var declaredMethods = type.GetDeclaredMethodsRecursively(); 23 | 24 | // Assert 25 | declaredMethods.Should().HaveCount(17); 26 | declaredMethods.Count(x => x.Name == "op_Implicit").Should().Be(2); 27 | declaredMethods.Count(x => x.Name == "op_Explicit").Should().Be(3); 28 | } 29 | 30 | [Fact] 31 | public void ShouldReturnAllMethodsOfDerivedType() 32 | { 33 | // Arrange 34 | var type = typeof(DerivedOperators); 35 | 36 | // Act 37 | var declaredMethods = type.GetDeclaredMethodsRecursively(); 38 | 39 | // Assert 40 | declaredMethods.Should().HaveCount(20); 41 | declaredMethods.Count(x => x.Name == "op_Implicit").Should().Be(2); 42 | declaredMethods.Count(x => x.Name == "op_Explicit").Should().Be(6); 43 | } 44 | 45 | [Fact] 46 | public void ShouldReturnAllMethodsOfDerivedTypeInfo() 47 | { 48 | // Arrange 49 | var typeInfo = typeof(DerivedOperators).GetTypeInfo(); 50 | 51 | // Act 52 | var declaredMethods = typeInfo.GetDeclaredMethodsRecursively(); 53 | 54 | // Assert 55 | declaredMethods.Should().HaveCount(20); 56 | declaredMethods.Count(x => x.Name == "op_Implicit").Should().Be(2); 57 | declaredMethods.Count(x => x.Name == "op_Explicit").Should().Be(6); 58 | } 59 | 60 | [Fact] 61 | public void IsSameOrParentShouldDetectSameType() 62 | { 63 | // Arrange 64 | var childTypeInfo = typeof(Operators).GetTypeInfo(); 65 | var parentTypeInfo = typeof(Operators).GetTypeInfo(); 66 | 67 | // Act 68 | var isSameOrParent = parentTypeInfo.IsSameOrParent(childTypeInfo); 69 | 70 | // Assert 71 | isSameOrParent.Should().BeTrue(); 72 | } 73 | 74 | [Fact] 75 | public void IsSameOrParentShouldDetectParent() 76 | { 77 | // Arrange 78 | var childTypeInfo = typeof(DerivedOperators).GetTypeInfo(); 79 | var parentTypeInfo = typeof(Operators).GetTypeInfo(); 80 | 81 | // Act 82 | var isSameOrParent = parentTypeInfo.IsSameOrParent(childTypeInfo); 83 | 84 | // Assert 85 | isSameOrParent.Should().BeTrue(); 86 | } 87 | } 88 | } -------------------------------------------------------------------------------- /TypeConverter.Tests/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.InteropServices; 3 | 4 | // General Information about an assembly is controlled through the following 5 | // set of attributes. Change these attribute values to modify the information 6 | // associated with an assembly. 7 | 8 | [assembly: AssemblyTitle("TypeConverter.Tests")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("TypeConverter.Tests")] 13 | [assembly: AssemblyCopyright("Copyright © 2017")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | 21 | [assembly: ComVisible(false)] 22 | 23 | // The following GUID is for the ID of the typelib if this project is exposed to COM 24 | 25 | [assembly: Guid("7a0d7d00-c346-451f-932d-fc2b8c8518c8")] 26 | 27 | // Version information for an assembly consists of the following four values: 28 | // 29 | // Major Version 30 | // Minor Version 31 | // Build Number 32 | // Revision 33 | // 34 | // You can specify all the values or you can default the Build and Revision Numbers 35 | // by using the '*' as shown below: 36 | // [assembly: AssemblyVersion("1.0.28")] 37 | 38 | [assembly: AssemblyVersion("1.0.28")] 39 | [assembly: AssemblyFileVersion("1.0.28-pre92")] 40 | -------------------------------------------------------------------------------- /TypeConverter.Tests/Stubs/DerivedOperators.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace TypeConverter.Tests.Stubs 4 | { 5 | public class DerivedOperators : Operators 6 | { 7 | public static explicit operator DateTime(DerivedOperators o) 8 | { 9 | return DateTime.Now; 10 | } 11 | 12 | public static explicit operator Byte(DerivedOperators o) 13 | { 14 | return (byte)0x08; 15 | } 16 | 17 | public static explicit operator Char(DerivedOperators o) 18 | { 19 | return 'X'; 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /TypeConverter.Tests/Stubs/MyEnum.cs: -------------------------------------------------------------------------------- 1 | namespace TypeConverter.Tests.Stubs 2 | { 3 | public enum MyEnum 4 | { 5 | Undefined, 6 | TestValue 7 | } 8 | } -------------------------------------------------------------------------------- /TypeConverter.Tests/Stubs/MyEnumConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace TypeConverter.Tests.Stubs 4 | { 5 | public class MyEnumConverter : IConvertable, IConvertable 6 | { 7 | public MyEnum Convert(string value) 8 | { 9 | return (MyEnum)Enum.Parse(typeof(MyEnum), value); 10 | } 11 | 12 | public string Convert(MyEnum value) 13 | { 14 | return value.ToString(); 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /TypeConverter.Tests/Stubs/Operators.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace TypeConverter.Tests.Stubs 4 | { 5 | public class Operators : IOperators, IGenericOperators 6 | { 7 | public static implicit operator string(Operators o) 8 | { 9 | return "Operators"; 10 | } 11 | 12 | public static implicit operator int(Operators o) 13 | { 14 | return 2; 15 | } 16 | 17 | public static explicit operator decimal?(Operators o) 18 | { 19 | return 3.456m; 20 | } 21 | 22 | public static explicit operator StringSplitOptions(Operators o) 23 | { 24 | return StringSplitOptions.RemoveEmptyEntries; 25 | } 26 | 27 | public static explicit operator Operators2(Operators o) 28 | { 29 | return new Operators2(); 30 | } 31 | 32 | public string GenericProperty 33 | { 34 | get 35 | { 36 | return "GenericProperty"; 37 | } 38 | } 39 | } 40 | 41 | public interface IOperators 42 | { 43 | } 44 | 45 | public interface IGenericOperators 46 | { 47 | T GenericProperty { get; } 48 | } 49 | } -------------------------------------------------------------------------------- /TypeConverter.Tests/Stubs/Operators2.cs: -------------------------------------------------------------------------------- 1 | 2 | using System; 3 | 4 | namespace TypeConverter.Tests.Stubs 5 | { 6 | public class Operators2 : IEquatable 7 | { 8 | public int Value { get { return 999; } } 9 | 10 | public static explicit operator bool(Operators2 o) 11 | { 12 | return false; 13 | } 14 | 15 | public static implicit operator Operators2(DerivedOperators o) 16 | { 17 | return null; 18 | } 19 | 20 | public static explicit operator Operators2(int i) 21 | { 22 | return new Operators2(); 23 | } 24 | 25 | public override bool Equals(object obj) 26 | { 27 | return this.Equals(obj as Operators2); 28 | } 29 | 30 | public override int GetHashCode() 31 | { 32 | return this.Value.GetHashCode(); 33 | } 34 | 35 | public bool Equals(Operators2 other) 36 | { 37 | return other != null && this.Value == other.Value; 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /TypeConverter.Tests/Stubs/OperatorsStruct.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace TypeConverter.Tests.Stubs 4 | { 5 | public struct OperatorsStruct 6 | { 7 | public static implicit operator string(OperatorsStruct o) 8 | { 9 | return "OperatorsStruct"; 10 | } 11 | 12 | public static implicit operator int(OperatorsStruct o) 13 | { 14 | return 1; 15 | } 16 | 17 | public static explicit operator decimal?(OperatorsStruct o) 18 | { 19 | return 1.0m; 20 | } 21 | 22 | public static explicit operator StringSplitOptions(OperatorsStruct o) 23 | { 24 | return StringSplitOptions.RemoveEmptyEntries; 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /TypeConverter.Tests/Stubs/TestStruct1.cs: -------------------------------------------------------------------------------- 1 | namespace TypeConverter.Tests.Stubs 2 | { 3 | public struct TestStruct1 4 | { 5 | public string TestString { get; set; } 6 | } 7 | } -------------------------------------------------------------------------------- /TypeConverter.Tests/TypeConverter.Tests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | Debug 7 | AnyCPU 8 | {74632D78-F918-401D-8197-F49E844F522B} 9 | Library 10 | Properties 11 | TypeConverter.Tests 12 | TypeConverter.Tests 13 | v4.5 14 | 512 15 | {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 16 | 10.0 17 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) 18 | $(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages 19 | False 20 | UnitTest 21 | 98a22752 22 | 23 | 24 | true 25 | full 26 | false 27 | bin\Debug\ 28 | DEBUG;TRACE 29 | prompt 30 | 4 31 | 32 | 33 | pdbonly 34 | true 35 | bin\Release\ 36 | TRACE 37 | prompt 38 | 4 39 | 40 | 41 | true 42 | 43 | 44 | ..\TypeConverter\TypeConverterKey.snk 45 | 46 | 47 | 48 | ..\packages\AutoMapper.4.2.1\lib\net45\AutoMapper.dll 49 | True 50 | 51 | 52 | ..\packages\Castle.Core.3.3.3\lib\net45\Castle.Core.dll 53 | True 54 | 55 | 56 | ..\packages\FluentAssertions.4.14.0\lib\net45\FluentAssertions.dll 57 | True 58 | 59 | 60 | ..\packages\FluentAssertions.4.14.0\lib\net45\FluentAssertions.Core.dll 61 | True 62 | 63 | 64 | 65 | ..\packages\Moq.4.5.21\lib\net45\Moq.dll 66 | True 67 | 68 | 69 | 70 | 71 | 72 | ..\packages\xunit.abstractions.2.0.0\lib\net35\xunit.abstractions.dll 73 | True 74 | 75 | 76 | ..\packages\xunit.assert.2.1.0\lib\portable-net45+win8+wp8+wpa81\xunit.assert.dll 77 | True 78 | 79 | 80 | ..\packages\xunit.extensibility.core.2.1.0\lib\portable-net45+win8+wp8+wpa81\xunit.core.dll 81 | True 82 | 83 | 84 | ..\packages\xunit.extensibility.execution.2.1.0\lib\net45\xunit.execution.desktop.dll 85 | True 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | Code 122 | 123 | 124 | 125 | 126 | 127 | TypeConverterKey.snk 128 | 129 | 130 | Designer 131 | 132 | 133 | 134 | 135 | 136 | {a6fcef44-d2ba-42c7-b3cb-13667bcd7b54} 137 | TypeConverter 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | False 146 | 147 | 148 | False 149 | 150 | 151 | False 152 | 153 | 154 | False 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. 164 | 165 | 166 | 167 | 168 | 175 | -------------------------------------------------------------------------------- /TypeConverter.Tests/TypeHelperTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Linq; 5 | 6 | using FluentAssertions; 7 | 8 | using TypeConverter.Extensions; 9 | using TypeConverter.Tests.Stubs; 10 | using TypeConverter.Tests.Utils; 11 | using TypeConverter.Utils; 12 | 13 | using Xunit; 14 | using Xunit.Abstractions; 15 | 16 | namespace TypeConverter.Tests 17 | { 18 | public class TypeHelperTests 19 | { 20 | private readonly ITestOutputHelper testOutputHelper; 21 | 22 | public TypeHelperTests(ITestOutputHelper testOutputHelper) 23 | { 24 | this.testOutputHelper = testOutputHelper; 25 | } 26 | 27 | [Fact] 28 | public void ShouldRunAllCasts() 29 | { 30 | CastTestRunner.RunTests(testCase => 31 | { 32 | // Arrange 33 | var value = CastTestRunner.GenerateValueForType(testCase.SourceType); 34 | var generatedTestSuccessful = CastTestRunner.CastValueWithGeneratedCode(value, testCase.SourceType, testCase.TargetType, testCase.CastFlag); 35 | 36 | // Act 37 | var castResult = TypeHelper.CastTo(value, testCase.TargetType); 38 | 39 | // Assert 40 | var isSuccessful = CastTestRunner.AreEqual( 41 | this.testOutputHelper, 42 | testCase.SourceType, 43 | testCase.TargetType, 44 | generatedTestSuccessful, 45 | castResult, 46 | testCase.CastFlag); 47 | 48 | if (!isSuccessful) 49 | { 50 | Debugger.Launch(); 51 | } 52 | 53 | return isSuccessful; 54 | }); 55 | } 56 | 57 | [Fact] 58 | public void ShouldGenerateValueForEachConsideredType() 59 | { 60 | // Arrange 61 | var allTypesToConsider = CastTestRunner.GetAllTestTypes().ToList(); 62 | var values = new List(allTypesToConsider.Count); 63 | 64 | // Act 65 | foreach (var type in allTypesToConsider) 66 | { 67 | var value = CastTestRunner.GenerateValueForType(type); 68 | this.testOutputHelper.WriteLine("Type: {0}, Value: {1}", type.GetFormattedName(), value); 69 | values.Add(value); 70 | } 71 | 72 | // Assert 73 | values.Should().HaveCount(allTypesToConsider.Count); 74 | } 75 | 76 | [Fact] 77 | public void ShouldSwollowInvalidProgramExceptionWhenNullableDecimalIsCastedToOperators2() 78 | { 79 | // Arrange 80 | var castFlag = CastFlag.Explicit; 81 | decimal? value = 1234m; 82 | 83 | // Act 84 | var castedValue = CastTestRunner.CastValueWithGeneratedCode(value, typeof(Nullable), typeof(Operators2), castFlag); 85 | 86 | // Assert 87 | castedValue.IsSuccessful.Should().BeTrue(); 88 | } 89 | 90 | [Fact] 91 | public void ShouldCastTypeWhichIsSubclassOfAnotherType() 92 | { 93 | // Arrange 94 | var castFlag = CastFlag.Implicit; 95 | var testCase = new CompilerConversionTestCase(typeof(DerivedOperators), typeof(Operators), castFlag); 96 | var value = CastTestRunner.GenerateValueForType(testCase.SourceType); 97 | var generatedTestSuccessful = CastTestRunner.CastValueWithGeneratedCode(value, testCase.SourceType, testCase.TargetType, castFlag); 98 | 99 | // Act 100 | var castResult = TypeHelper.CastTo(value, testCase.TargetType); 101 | 102 | // Assert 103 | var isSuccessful = CastTestRunner.AreEqual(this.testOutputHelper, testCase.SourceType, testCase.TargetType, generatedTestSuccessful, castResult, castFlag); 104 | isSuccessful.Should().BeTrue(); 105 | } 106 | 107 | [Fact] 108 | public void ShouldCastTypeWhichImplementsAGenericInterface() 109 | { 110 | // Arrange 111 | var castFlag = CastFlag.Implicit; 112 | var testCase = new CompilerConversionTestCase(typeof(Operators), typeof(IGenericOperators), castFlag); 113 | var value = CastTestRunner.GenerateValueForType(testCase.SourceType); 114 | var generatedTestSuccessful = CastTestRunner.CastValueWithGeneratedCode(value, testCase.SourceType, testCase.TargetType, castFlag); 115 | 116 | // Act 117 | var castResult = TypeHelper.CastTo(value, testCase.TargetType); 118 | 119 | // Assert 120 | var isSuccessful = CastTestRunner.AreEqual(this.testOutputHelper, testCase.SourceType, testCase.TargetType, generatedTestSuccessful, castResult, castFlag); 121 | isSuccessful.Should().BeTrue(); 122 | } 123 | 124 | [Fact] 125 | public void ShouldNotCastTypeWhichImplementsAGenericInterfaceWithNoGenericTypeArgumentsDefined() 126 | { 127 | // Arrange 128 | var castFlag = CastFlag.Implicit; 129 | var testCase = new CompilerConversionTestCase(typeof(Operators), typeof(IGenericOperators<>), castFlag); 130 | var value = CastTestRunner.GenerateValueForType(testCase.SourceType); 131 | var generatedTestSuccessful = CastTestRunner.CastValueWithGeneratedCode(value, testCase.SourceType, testCase.TargetType, castFlag); 132 | 133 | // Act 134 | var castResult = TypeHelper.CastTo(value, testCase.TargetType); 135 | 136 | // Assert 137 | var isSuccessful = CastTestRunner.AreEqual(this.testOutputHelper, testCase.SourceType, testCase.TargetType, generatedTestSuccessful, castResult, castFlag); 138 | isSuccessful.Should().BeTrue(); 139 | } 140 | 141 | [Fact] 142 | public void ShouldEqualOperators2Instances() 143 | { 144 | // Arrange 145 | Operators2 o1 = new Operators2(); 146 | Operators2 o2 = new Operators2(); 147 | 148 | // Act 149 | var objectEquals = Equals(o1, o2); 150 | var operatorsEquals = o1.Equals(o2); 151 | 152 | // Assert 153 | objectEquals.Should().BeTrue(); 154 | operatorsEquals.Should().BeTrue(); 155 | } 156 | } 157 | } -------------------------------------------------------------------------------- /TypeConverter.Tests/Utils/CastTestRunner.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.CodeDom.Compiler; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Reflection; 6 | 7 | using Microsoft.CSharp; 8 | 9 | using TypeConverter.Extensions; 10 | using TypeConverter.Tests.Stubs; 11 | using TypeConverter.Utils; 12 | 13 | using Xunit; 14 | using Xunit.Abstractions; 15 | 16 | namespace TypeConverter.Tests.Utils 17 | { 18 | internal class CastTestRunner 19 | { 20 | internal static void RunTests(Func runTestCase) 21 | { 22 | var allTypesToConsider = GetAllTestTypes(); 23 | var testCasesImplicit = GenerateTestCases(allTypesToConsider, CastFlag.Implicit); 24 | var testCasesExplicit = GenerateTestCases(allTypesToConsider, CastFlag.Explicit); 25 | var testCases = testCasesImplicit.Concat(testCasesExplicit); 26 | 27 | var mistakes = new List(); 28 | foreach (var testCase in testCases.Where(tc => tc.IsCompilable)) 29 | { 30 | var isSuccess = runTestCase(testCase); 31 | if (!isSuccess) 32 | { 33 | mistakes.Add( 34 | string.Format("{0} => {1}: failed to {2} cast", 35 | testCase.SourceType.GetFormattedName(), 36 | testCase.TargetType.GetFormattedName(), 37 | testCase.CastFlag == CastFlag.Explicit ? "implicitly" : "explicitly")); 38 | } 39 | } 40 | Assert.True(mistakes.Count == 0, string.Join(Environment.NewLine, new[] { mistakes.Count + " errors" }.Concat(mistakes))); 41 | } 42 | 43 | internal static IEnumerable GetAllTestTypes() 44 | { 45 | var primitives = typeof(object).GetTypeInfo().Assembly.DefinedTypes.Where(t => t.IsPrimitive).ToArray(); 46 | var simpleTypes = new[] 47 | { 48 | typeof(string), 49 | typeof(DateTime), 50 | typeof(decimal), 51 | typeof(object), 52 | typeof(DateTimeOffset), 53 | typeof(TimeSpan), 54 | typeof(StringSplitOptions), 55 | typeof(DateTimeKind) 56 | }; 57 | var variantTypes = new[] 58 | { 59 | typeof(string[]), 60 | typeof(object[]), 61 | typeof(IEnumerable), 62 | typeof(IEnumerable), 63 | typeof(Func), 64 | typeof(Func), 65 | typeof(Action), 66 | typeof(Action) 67 | }; 68 | var conversionOperators = new[] 69 | { 70 | typeof(Operators), 71 | typeof(IOperators), 72 | typeof(IGenericOperators<>), 73 | typeof(IGenericOperators), 74 | typeof(Operators2), 75 | typeof(DerivedOperators), 76 | typeof(OperatorsStruct) 77 | }; 78 | var typesToConsider = primitives.Concat(simpleTypes) 79 | .Concat(variantTypes) 80 | .Concat(conversionOperators).ToArray(); 81 | var nullableTypes = typesToConsider.Where(type => type.IsValueType).Select(t => typeof(Nullable<>).MakeGenericType(t)); 82 | var allTypesToConsider = typesToConsider.Concat(nullableTypes); 83 | 84 | return allTypesToConsider; 85 | } 86 | 87 | private static List GenerateTestCases(IEnumerable allTypes, CastFlag castFlag) 88 | { 89 | 90 | // generate cross product of given types 91 | var typeCrossProduct = allTypes.SelectMany(type => allTypes, (sourceType, targetType) => new { sourceType, targetType }) 92 | .Select((type, index) => 93 | new { 94 | type.sourceType, 95 | type.targetType, 96 | index, 97 | codeline = GetCodeline(index, type.sourceType, type.targetType, castFlag) 98 | }).ToArray(); 99 | 100 | // create the code to pass to the compiler 101 | var code = string.Join( 102 | Environment.NewLine, 103 | new[] { "namespace A { public class B { static T Get() { return default(T); } public void C() {" }.Concat( 104 | typeCrossProduct.Select(t => t.codeline)) 105 | .Concat(new[] { "}}}" })); 106 | 107 | // compile the code 108 | var provider = new CSharpCodeProvider(); 109 | var compilerParams = new CompilerParameters(); 110 | compilerParams.ReferencedAssemblies.Add(typeof(CastTestRunner).Assembly.Location); // reference the current assembly! 111 | compilerParams.GenerateExecutable = false; 112 | compilerParams.GenerateInMemory = true; 113 | var compilationResult = provider.CompileAssemblyFromSource(compilerParams, code); 114 | 115 | // determine the outcome of each conversion by matching compiler errors with conversions by line # 116 | var testCases = typeCrossProduct.GroupJoin(compilationResult.Errors.Cast(), 117 | type => type.index, 118 | e => e.Line - 2, 119 | (type, errors) => new CompilerConversionTestCase(type.sourceType, type.targetType, castFlag, type.codeline, errors.FirstOrDefault())) 120 | .ToList(); 121 | 122 | // add a special case 123 | // this can't be verified by the normal means, since it's a private class 124 | ////testCases.Add(new CompilerConversionTestCase(typeof(PrivateOperators), typeof(int), string.Empty, default(CompilerError))); 125 | 126 | return testCases; 127 | } 128 | 129 | private static string GetCodeline(int index, Type sourceType, Type targetType, CastFlag castFlag) 130 | { 131 | return string.Format( 132 | "{0} var{1} = {2}Get<{3}>();", 133 | targetType.GetFormattedFullname(), 134 | index, 135 | castFlag == CastFlag.Implicit ? string.Empty : "(" + targetType.GetFormattedFullname() + ")", 136 | sourceType.GetFormattedFullname()); 137 | } 138 | 139 | internal static CastResult CastValueWithGeneratedCode(object value, Type sourceType, Type targetType, CastFlag castFlag) 140 | { 141 | string className = "GeneratedTestClass"; 142 | string methodName = "RunTest"; 143 | string castLine = string.Format( 144 | "{0} x = {1}value; return x;", 145 | targetType.GetFormattedFullname(), 146 | castFlag == CastFlag.Implicit ? string.Empty : "(" + targetType.GetFormattedFullname() + ")"); 147 | 148 | string code = "public class " + className + " { public " + targetType.GetFormattedFullname() + " " + methodName + "(" + sourceType.GetFormattedFullname() + " value)" + 149 | "{" + castLine + "}}"; 150 | 151 | using (CSharpCodeProvider provider = new CSharpCodeProvider()) 152 | { 153 | var compilerParams = new CompilerParameters(); 154 | compilerParams.ReferencedAssemblies.Add(typeof(CastTestRunner).Assembly.Location); 155 | compilerParams.GenerateExecutable = false; 156 | compilerParams.GenerateInMemory = true; 157 | var compilationResult = provider.CompileAssemblyFromSource(compilerParams, code); 158 | if (compilationResult.Errors.HasErrors) 159 | { 160 | 161 | var compilerException = new AggregateException("CastValueWithGeneratedCode failed to generate test class.", 162 | compilationResult.Errors 163 | .OfType() 164 | .Where(e => !e.IsWarning) 165 | .Select(e => new CompilerException(e.Line, e.Column, e.ErrorText))); 166 | return new CastResult(compilerException, castFlag); 167 | } 168 | 169 | var generatedClass = compilationResult.CompiledAssembly.GetType(className); 170 | 171 | var instance = Activator.CreateInstance(generatedClass); 172 | var testMethod = generatedClass.GetMethod(methodName); 173 | 174 | try 175 | { 176 | var castedValue = testMethod.Invoke(instance, new[] { value }); 177 | return new CastResult(castedValue, castFlag); 178 | } 179 | catch (TargetInvocationException ex) 180 | { 181 | if (ex.InnerException is InvalidProgramException) 182 | { 183 | // This is most probably an error in Roslyn compiler. 184 | // See http://stackoverflow.com/questions/18342943/serious-bugs-with-lifted-nullable-conversions-from-int-allowing-conversion-from 185 | return new CastResult(value, castFlag); 186 | } 187 | 188 | return new CastResult(ex, castFlag); 189 | } 190 | catch (Exception ex) 191 | { 192 | return new CastResult(ex, castFlag); 193 | } 194 | } 195 | } 196 | 197 | private class PrivateOperators 198 | { 199 | public static implicit operator int(PrivateOperators o) 200 | { 201 | return 1; 202 | } 203 | } 204 | 205 | internal static object GenerateValueForType(Type type) 206 | { 207 | if (type == typeof(object)) 208 | { 209 | return new object(); 210 | } 211 | if (type == typeof(bool)) 212 | { 213 | return true; 214 | } 215 | if (type == typeof(bool?)) 216 | { 217 | return true; 218 | } 219 | if (type == typeof(byte) || type == typeof(byte?)) 220 | { 221 | return (byte)0x99; 222 | } 223 | if (type == typeof(char) || type == typeof(char?)) 224 | { 225 | return 'c'; 226 | } 227 | if (type == typeof(double) || type == typeof(double?)) 228 | { 229 | return 1.234d; 230 | } 231 | if (type == typeof(Single)) 232 | { 233 | return (Single)1234; 234 | } 235 | if (type == typeof(Single?)) 236 | { 237 | return new Single?(1234); 238 | } 239 | if (type == typeof(Int16)) 240 | { 241 | return (Int16)1234; 242 | } 243 | if (type == typeof(Int16?)) 244 | { 245 | return (Int16?)1234; 246 | } 247 | if (type == typeof(UInt16) || type == typeof(UInt16?)) 248 | { 249 | return (UInt16)1234; 250 | } 251 | if (type == typeof(Int32) || type == typeof(Int32?)) 252 | { 253 | return (Int32)1234; 254 | } 255 | if (type == typeof(UInt32) || type == typeof(UInt32?)) 256 | { 257 | return (UInt32)1234; 258 | } 259 | if (type == typeof(Int64) || type == typeof(Int64?)) 260 | { 261 | return (Int64)1234; 262 | } 263 | if (type == typeof(UInt64) || type == typeof(UInt64?)) 264 | { 265 | return (UInt64)1234; 266 | } 267 | if (type == typeof(IntPtr) || type == typeof(IntPtr?)) 268 | { 269 | return new IntPtr(1234); 270 | } 271 | if (type == typeof(UIntPtr) || type == typeof(UIntPtr?)) 272 | { 273 | return new UIntPtr(1234); 274 | } 275 | if (type == typeof(SByte) || type == typeof(SByte?)) 276 | { 277 | return new SByte(); 278 | } 279 | if (type == typeof(string)) 280 | { 281 | return "asdflkj"; 282 | } 283 | if (type == typeof(decimal) || type == typeof(decimal?)) 284 | { 285 | return 1.234m; 286 | } 287 | if (type == typeof(DateTime) || type == typeof(DateTime?)) 288 | { 289 | return DateTime.MaxValue; 290 | } 291 | if (type == typeof(DateTimeOffset) || type == typeof(DateTimeOffset?)) 292 | { 293 | return DateTimeOffset.MaxValue; 294 | } 295 | if (type == typeof(TimeSpan) || type == typeof(TimeSpan?)) 296 | { 297 | return new TimeSpan(1, 2, 3, 4); 298 | } 299 | if (type == typeof(StringSplitOptions) || type == typeof(StringSplitOptions?)) 300 | { 301 | return StringSplitOptions.RemoveEmptyEntries; 302 | } 303 | if (type == typeof(DateTimeKind) || type == typeof(DateTimeKind?)) 304 | { 305 | return DateTimeKind.Utc; 306 | } 307 | if (type == typeof(OperatorsStruct)) 308 | { 309 | return new OperatorsStruct(); 310 | } 311 | if (type == typeof(OperatorsStruct?)) 312 | { 313 | return new OperatorsStruct(); 314 | } 315 | if (type == typeof(Operators) || type == typeof(IOperators) || type == typeof(IGenericOperators<>) || type == typeof(IGenericOperators)) 316 | { 317 | return new Operators(); 318 | } 319 | if (type == typeof(Operators2)) 320 | { 321 | return new Operators2(); 322 | } 323 | if (type == typeof(DerivedOperators)) 324 | { 325 | return new DerivedOperators(); 326 | } 327 | if (type == typeof(PrivateOperators)) 328 | { 329 | return new PrivateOperators(); 330 | } 331 | if (type == typeof(string[])) 332 | { 333 | return new[] { "a", "b" }; 334 | } 335 | if (type == typeof(object[])) 336 | { 337 | return new[] { new object(), new object() }; 338 | } 339 | if (type == typeof(IEnumerable)) 340 | { 341 | return new List(); 342 | } 343 | if (type == typeof(IEnumerable)) 344 | { 345 | return new List(); 346 | } 347 | if (type == typeof(Func)) 348 | { 349 | return new Func(() => ""); 350 | } 351 | if (type == typeof(Func)) 352 | { 353 | return new Func(() => new object()); 354 | } 355 | if (type == typeof(Action)) 356 | { 357 | return new Action(x => { }); 358 | } 359 | if (type == typeof(Action)) 360 | { 361 | return new Action(x => { }); 362 | } 363 | 364 | throw new InvalidOperationException(string.Format("Could not generate an instance of type {0}. Please register.", type.GetFormattedName())); 365 | } 366 | 367 | internal static bool AreEqual(ITestOutputHelper testOutputHelper, Type sourceType, Type targetType, CastResult compilerResult, CastResult castResult, CastFlag expectedCastFlag) 368 | { 369 | if (compilerResult.IsSuccessful == true && castResult.IsSuccessful == false) 370 | { 371 | // Let's assert the details if the compiler generates a successful result 372 | // but the CastTo method does not the same. 373 | 374 | var castFlagsAreEqual = compilerResult.CastFlag == castResult.CastFlag || castResult.CastFlag == CastFlag.Implicit; 375 | if (!castFlagsAreEqual) 376 | { 377 | testOutputHelper.WriteLine("CastFlags of conversion between {0} and {1} are not equal." + Environment.NewLine + 378 | "Expected CastFlag: {2}" + Environment.NewLine + 379 | "Resulted CastFlag: {3}" + Environment.NewLine, 380 | sourceType.GetFormattedName(), 381 | targetType.GetFormattedName(), 382 | expectedCastFlag, 383 | castResult.CastFlag); 384 | return false; 385 | } 386 | 387 | var valuesAreNotEqual = compilerResult.CastFlag == castResult.CastFlag && !Equals(compilerResult.Value, castResult.Value); 388 | if (valuesAreNotEqual) 389 | { 390 | testOutputHelper.WriteLine("Result of {0} conversion between {1} and {2} are not equal.", 391 | expectedCastFlag == CastFlag.Implicit ? "implicit" : "explicit", 392 | sourceType.GetFormattedName(), 393 | targetType.GetFormattedName()); 394 | 395 | return false; 396 | } 397 | } 398 | 399 | return true; 400 | } 401 | } 402 | } 403 | -------------------------------------------------------------------------------- /TypeConverter.Tests/Utils/CompilerConversionTestCase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.CodeDom.Compiler; 3 | using System.Diagnostics; 4 | 5 | using TypeConverter.Utils; 6 | 7 | namespace TypeConverter.Tests.Utils 8 | { 9 | [DebuggerDisplay("SourceType = {SourceType}, TargetType = {TargetType}, IsCompilable = {IsCompilable}")] 10 | internal class CompilerConversionTestCase 11 | { 12 | public CompilerConversionTestCase(Type sourceType, Type targetType, CastFlag castFlag, string codeline = null, CompilerError compilerError = null) 13 | { 14 | this.SourceType = sourceType; 15 | this.TargetType = targetType; 16 | this.Codeline = codeline; 17 | this.CompilerError = compilerError; 18 | this.CastFlag = castFlag; 19 | } 20 | 21 | public CastFlag CastFlag { get; private set; } 22 | 23 | public Type SourceType { get; private set; } 24 | 25 | public Type TargetType { get; private set; } 26 | 27 | public string Codeline { get; private set; } 28 | 29 | public CompilerError CompilerError { get; private set; } 30 | 31 | public bool IsCompilable 32 | { 33 | get 34 | { 35 | return this.CompilerError == null; 36 | } 37 | } 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /TypeConverter.Tests/Utils/CompilerException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | 4 | namespace TypeConverter.Tests.Utils 5 | { 6 | [DebuggerDisplay("Line = {Line}, Column = {Column}, ErrorText = {ErrorText}")] 7 | internal class CompilerException : Exception 8 | { 9 | public CompilerException(int line, int column, string errorText) 10 | { 11 | this.Line = line; 12 | this.Column = column; 13 | this.ErrorText = errorText; 14 | } 15 | 16 | public int Line { get; private set; } 17 | 18 | public int Column { get; private set; } 19 | 20 | public string ErrorText { get; private set; } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /TypeConverter.Tests/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /TypeConverter.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.25420.1 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "nuget", "nuget", "{B5AEA046-0E87-4A7B-8F11-80B6BDDA1F2A}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TypeConverter.NuGet", "TypeConverter.NuGet\TypeConverter.NuGet.csproj", "{2CF5E868-1098-4C6D-9001-27D8DC0D87BE}" 9 | ProjectSection(ProjectDependencies) = postProject 10 | {A6FCEF44-D2BA-42C7-B3CB-13667BCD7B54} = {A6FCEF44-D2BA-42C7-B3CB-13667BCD7B54} 11 | EndProjectSection 12 | EndProject 13 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TypeConverter", "TypeConverter\TypeConverter.csproj", "{A6FCEF44-D2BA-42C7-B3CB-13667BCD7B54}" 14 | EndProject 15 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TypeConverter.Tests", "TypeConverter.Tests\TypeConverter.Tests.csproj", "{74632D78-F918-401D-8197-F49E844F522B}" 16 | EndProject 17 | Global 18 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 19 | Debug|Any CPU = Debug|Any CPU 20 | Debug|ARM = Debug|ARM 21 | Debug|x64 = Debug|x64 22 | Debug|x86 = Debug|x86 23 | Release|Any CPU = Release|Any CPU 24 | Release|ARM = Release|ARM 25 | Release|x64 = Release|x64 26 | Release|x86 = Release|x86 27 | EndGlobalSection 28 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 29 | {2CF5E868-1098-4C6D-9001-27D8DC0D87BE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 30 | {2CF5E868-1098-4C6D-9001-27D8DC0D87BE}.Debug|Any CPU.Build.0 = Debug|Any CPU 31 | {2CF5E868-1098-4C6D-9001-27D8DC0D87BE}.Debug|ARM.ActiveCfg = Debug|Any CPU 32 | {2CF5E868-1098-4C6D-9001-27D8DC0D87BE}.Debug|x64.ActiveCfg = Debug|Any CPU 33 | {2CF5E868-1098-4C6D-9001-27D8DC0D87BE}.Debug|x86.ActiveCfg = Debug|Any CPU 34 | {2CF5E868-1098-4C6D-9001-27D8DC0D87BE}.Release|Any CPU.ActiveCfg = Release|Any CPU 35 | {2CF5E868-1098-4C6D-9001-27D8DC0D87BE}.Release|Any CPU.Build.0 = Release|Any CPU 36 | {2CF5E868-1098-4C6D-9001-27D8DC0D87BE}.Release|ARM.ActiveCfg = Release|Any CPU 37 | {2CF5E868-1098-4C6D-9001-27D8DC0D87BE}.Release|x64.ActiveCfg = Release|Any CPU 38 | {2CF5E868-1098-4C6D-9001-27D8DC0D87BE}.Release|x86.ActiveCfg = Release|Any CPU 39 | {A6FCEF44-D2BA-42C7-B3CB-13667BCD7B54}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 40 | {A6FCEF44-D2BA-42C7-B3CB-13667BCD7B54}.Debug|Any CPU.Build.0 = Debug|Any CPU 41 | {A6FCEF44-D2BA-42C7-B3CB-13667BCD7B54}.Debug|ARM.ActiveCfg = Debug|Any CPU 42 | {A6FCEF44-D2BA-42C7-B3CB-13667BCD7B54}.Debug|x64.ActiveCfg = Debug|Any CPU 43 | {A6FCEF44-D2BA-42C7-B3CB-13667BCD7B54}.Debug|x86.ActiveCfg = Debug|Any CPU 44 | {A6FCEF44-D2BA-42C7-B3CB-13667BCD7B54}.Release|Any CPU.ActiveCfg = Release|Any CPU 45 | {A6FCEF44-D2BA-42C7-B3CB-13667BCD7B54}.Release|Any CPU.Build.0 = Release|Any CPU 46 | {A6FCEF44-D2BA-42C7-B3CB-13667BCD7B54}.Release|ARM.ActiveCfg = Release|Any CPU 47 | {A6FCEF44-D2BA-42C7-B3CB-13667BCD7B54}.Release|x64.ActiveCfg = Release|Any CPU 48 | {A6FCEF44-D2BA-42C7-B3CB-13667BCD7B54}.Release|x86.ActiveCfg = Release|Any CPU 49 | {74632D78-F918-401D-8197-F49E844F522B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 50 | {74632D78-F918-401D-8197-F49E844F522B}.Debug|Any CPU.Build.0 = Debug|Any CPU 51 | {74632D78-F918-401D-8197-F49E844F522B}.Debug|ARM.ActiveCfg = Debug|Any CPU 52 | {74632D78-F918-401D-8197-F49E844F522B}.Debug|x64.ActiveCfg = Debug|Any CPU 53 | {74632D78-F918-401D-8197-F49E844F522B}.Debug|x86.ActiveCfg = Debug|Any CPU 54 | {74632D78-F918-401D-8197-F49E844F522B}.Release|Any CPU.ActiveCfg = Release|Any CPU 55 | {74632D78-F918-401D-8197-F49E844F522B}.Release|Any CPU.Build.0 = Release|Any CPU 56 | {74632D78-F918-401D-8197-F49E844F522B}.Release|ARM.ActiveCfg = Release|Any CPU 57 | {74632D78-F918-401D-8197-F49E844F522B}.Release|x64.ActiveCfg = Release|Any CPU 58 | {74632D78-F918-401D-8197-F49E844F522B}.Release|x86.ActiveCfg = Release|Any CPU 59 | EndGlobalSection 60 | GlobalSection(SolutionProperties) = preSolution 61 | HideSolutionNode = FALSE 62 | EndGlobalSection 63 | GlobalSection(NestedProjects) = preSolution 64 | {2CF5E868-1098-4C6D-9001-27D8DC0D87BE} = {B5AEA046-0E87-4A7B-8F11-80B6BDDA1F2A} 65 | EndGlobalSection 66 | EndGlobal 67 | -------------------------------------------------------------------------------- /TypeConverter/Attempts/CastAttempt.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | using TypeConverter.Utils; 4 | 5 | namespace TypeConverter.Attempts 6 | { 7 | // Attempt 2: Use implicit or explicit casting if supported 8 | internal class CastAttempt : IConversionAttempt 9 | { 10 | public ConversionResult TryConvert(object value, Type sourceType, Type targetType) 11 | { 12 | var castedValue = TypeHelper.CastTo(value, targetType); 13 | return castedValue; 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /TypeConverter/Attempts/ChangeTypeAttempt.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace TypeConverter.Attempts 4 | { 5 | // Attempt 3: Use System.Convert.ChangeType to change value to targetType 6 | internal class ChangeTypeAttempt : IConversionAttempt 7 | { 8 | public ConversionResult TryConvert(object value, Type sourceType, Type targetType) 9 | { 10 | try 11 | { 12 | if (Nullable.GetUnderlyingType(targetType) != null) 13 | { 14 | return this.TryConvert(value, sourceType, Nullable.GetUnderlyingType(targetType)); 15 | } 16 | 17 | // ChangeType basically does some conversion checks 18 | // and then tries to perform the according Convert.ToWhatever(value) method. 19 | // See: http://referencesource.microsoft.com/#mscorlib/system/convert.cs,3bcca7a9bda4114e 20 | return new ConversionResult(Convert.ChangeType(value, targetType)); 21 | } 22 | catch (Exception ex) 23 | { 24 | return new ConversionResult(ex); 25 | } 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /TypeConverter/Attempts/CustomConvertAttempt.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection; 5 | 6 | using TypeConverter.Utils; 7 | 8 | namespace TypeConverter.Attempts 9 | { 10 | // Attempt 1: Try to convert using registered converter 11 | // Having TryConvertGenericallyUsingConverterStrategy as a first attempt, the user of this library has the chance 12 | // to influence the conversion process with first priority. 13 | internal class CustomConvertAttempt : IConversionAttempt 14 | { 15 | private readonly Dictionary, Func> converters; 16 | 17 | public CustomConvertAttempt() 18 | { 19 | this.converters = new Dictionary, Func>(); 20 | } 21 | 22 | internal void RegisterConverter(IConvertable converter) 23 | { 24 | lock (this.converters) 25 | { 26 | var convertables = converter.GetType().GetTypeInfo().ImplementedInterfaces.Where(i => 27 | i.GetTypeInfo().IsGenericType && 28 | i.GetGenericTypeDefinition() == typeof(IConvertable<,>)); 29 | 30 | foreach (var convertable in convertables) 31 | { 32 | var sourceType = convertable.GenericTypeArguments[0]; 33 | var targetType = convertable.GenericTypeArguments[1]; 34 | this.converters.Add(new Tuple(sourceType, targetType), () => converter); 35 | } 36 | } 37 | } 38 | 39 | internal void RegisterConverter(Func> converterFactory) 40 | { 41 | lock (this.converters) 42 | { 43 | this.converters.Add(new Tuple(typeof(TSource), typeof(TTarget)), converterFactory); 44 | } 45 | } 46 | 47 | public ConversionResult TryConvert(object value, Type sourceType, Type targetType) 48 | { 49 | if (sourceType.GetTypeInfo().ContainsGenericParameters || targetType.GetTypeInfo().ContainsGenericParameters) 50 | { 51 | // Cannot deal with open generics, like IGenericOperators<> 52 | return null; 53 | } 54 | 55 | // Call generic method GetConverterForType to retrieve generic IConverter 56 | var getConverterForTypeMethod = ReflectionHelper.GetMethod(() => this.GetConverterForType()).GetGenericMethodDefinition(); 57 | var genericGetConverterForTypeMethod = getConverterForTypeMethod.MakeGenericMethod(sourceType, targetType); 58 | 59 | var genericConverter = genericGetConverterForTypeMethod.Invoke(this, null); 60 | if (genericConverter == null) 61 | { 62 | return null; 63 | } 64 | 65 | var matchingConverterInterface = genericConverter.GetType().GetTypeInfo().ImplementedInterfaces.SingleOrDefault(i => 66 | i.GenericTypeArguments.Length == 2 && 67 | i.GenericTypeArguments[0] == sourceType && 68 | i.GenericTypeArguments[1] == targetType); 69 | 70 | // Call Convert method on the particular interface 71 | var convertMethodGeneric = matchingConverterInterface.GetTypeInfo().GetDeclaredMethod("Convert"); 72 | var convertedValue = convertMethodGeneric.Invoke(genericConverter, new[] { value }); 73 | return new ConversionResult(convertedValue); 74 | } 75 | 76 | internal IConvertable GetConverterForType() 77 | { 78 | lock (this.converters) 79 | { 80 | var key = new Tuple(typeof(TSource), typeof(TTarget)); 81 | if (this.converters.ContainsKey(key)) 82 | { 83 | var converterFactory = this.converters[key]; 84 | return (IConvertable)converterFactory(); 85 | } 86 | 87 | return null; 88 | } 89 | } 90 | 91 | internal void Reset() 92 | { 93 | lock (this.converters) 94 | { 95 | this.converters.Clear(); 96 | } 97 | } 98 | } 99 | } -------------------------------------------------------------------------------- /TypeConverter/Attempts/EnumParseAttempt.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reflection; 3 | 4 | namespace TypeConverter.Attempts 5 | { 6 | // Attempt 4: Try to convert generic enum 7 | internal class EnumParseAttempt : IConversionAttempt 8 | { 9 | public ConversionResult TryConvert(object value, Type sourceType, Type targetType) 10 | { 11 | if (sourceType.GetTypeInfo().IsEnum) 12 | { 13 | return new ConversionResult(value.ToString()); 14 | } 15 | 16 | if (targetType.GetTypeInfo().IsEnum) 17 | { 18 | try 19 | { 20 | return new ConversionResult(Enum.Parse(targetType, value.ToString(), true)); 21 | } 22 | catch (ArgumentException) 23 | { 24 | // Unfortunately, we cannot use Enum.TryParse in this case, 25 | // The only way to catch failing parses is this ugly try-catch 26 | return null; 27 | } 28 | } 29 | 30 | return null; 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /TypeConverter/Attempts/IConversionAttempt.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace TypeConverter.Attempts 4 | { 5 | /// 6 | /// Abstraction of a conversion attempt. 7 | /// 8 | internal interface IConversionAttempt 9 | { 10 | ConversionResult TryConvert(object value, Type sourceType, Type targetType); 11 | } 12 | } -------------------------------------------------------------------------------- /TypeConverter/Attempts/MapAttempt.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq.Expressions; 4 | using System.Reflection; 5 | 6 | using TypeConverter.Extensions; 7 | using TypeConverter.Utils; 8 | 9 | namespace TypeConverter.Attempts 10 | { 11 | internal class MapAttempt : IConversionAttempt 12 | { 13 | private readonly Dictionary, List>> mapping; 14 | 15 | public MapAttempt() 16 | { 17 | this.mapping = new Dictionary, List>>(); 18 | } 19 | 20 | public ConversionResult TryConvert(object value, Type sourceType, Type targetType) 21 | { 22 | object target = null; 23 | try 24 | { 25 | target = Activator.CreateInstance(targetType); 26 | var map = this.GetMappingForTypes(sourceType, targetType); 27 | if (map == null) 28 | { 29 | return new ConversionResult(new Exception($"Mapping for sourceType {sourceType.GetFormattedName()} and targetType {targetType.GetFormattedName()} does not exist.")); 30 | } 31 | 32 | foreach (var m in map) 33 | { 34 | var sourceValue = m.Item1.GetValue(value); 35 | m.Item2.SetValue(target, sourceValue); 36 | } 37 | 38 | } 39 | catch (Exception ex) 40 | { 41 | return new ConversionResult(ex); 42 | } 43 | 44 | return new ConversionResult(target); 45 | } 46 | 47 | private List> GetMappingForTypes(Type sourceType, Type targetType) 48 | { 49 | lock (this.mapping) 50 | { 51 | var key = new Tuple(sourceType, targetType); 52 | if (this.mapping.ContainsKey(key)) 53 | { 54 | return this.mapping[key]; 55 | } 56 | 57 | return null; 58 | } 59 | } 60 | 61 | public void RegisterMapping(Expression> destinationMember, Expression> sourceMember) 62 | { 63 | var sourcePropertyInfo = ReflectionHelper.GetPropertyInfo(sourceMember); 64 | var destinationPropertyInfo = ReflectionHelper.GetPropertyInfo(destinationMember); 65 | 66 | lock (this.mapping) 67 | { 68 | var map = this.GetMappingForTypes(typeof(TSource), typeof(TTarget)); 69 | if (map == null) 70 | { 71 | this.mapping.Add(new Tuple(typeof(TSource), typeof(TTarget)), new List> { new Tuple(sourcePropertyInfo, destinationPropertyInfo) }); 72 | } 73 | else 74 | { 75 | map.Add(new Tuple(sourcePropertyInfo, destinationPropertyInfo)); 76 | } 77 | } 78 | } 79 | 80 | internal void Reset() 81 | { 82 | lock (this.mapping) 83 | { 84 | this.mapping.Clear(); 85 | } 86 | } 87 | } 88 | } -------------------------------------------------------------------------------- /TypeConverter/Attempts/StringParseAttempt.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reflection; 3 | 4 | namespace TypeConverter.Attempts 5 | { 6 | // Attempt 5: We essentially make a guess that to convert from a string 7 | // to an arbitrary type T there will be a static method defined on type T called Parse 8 | // that will take an argument of type string. i.e. T.Parse(string)->T we call this 9 | // method to convert the string to the type required by the property. 10 | internal class StringParseAttempt : IConversionAttempt 11 | { 12 | public ConversionResult TryConvert(object value, Type sourceType, Type targetType) 13 | { 14 | // Either of both, sourceType or targetType, need to be typeof(string) 15 | if (sourceType == typeof(string) && targetType != typeof(string)) 16 | { 17 | var parseMethod = targetType.GetRuntimeMethod("Parse", new[] { sourceType }); 18 | if (parseMethod != null) 19 | { 20 | try 21 | { 22 | return new ConversionResult(parseMethod.Invoke(this, new[] { value })); 23 | } 24 | catch (Exception) 25 | { 26 | return null; 27 | } 28 | } 29 | } 30 | else if (targetType == typeof(string) && sourceType != typeof(string)) 31 | { 32 | return new ConversionResult(value.ToString()); 33 | } 34 | 35 | return null; 36 | } 37 | } 38 | } -------------------------------------------------------------------------------- /TypeConverter/Caching/CacheManager.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | using TypeConverter.Attempts; 6 | 7 | namespace TypeConverter.Caching 8 | { 9 | /// 10 | /// CacheManager is an internal data structure which stores conversion strategies 11 | /// for type-to-type mappings. 12 | /// 13 | internal class CacheManager 14 | { 15 | private Dictionary, WeightedCacheResult> cache = new Dictionary, WeightedCacheResult>(); 16 | private readonly object syncObj = new object(); 17 | private bool isMaxCacheSizeEnabled; 18 | 19 | public CacheManager() 20 | { 21 | this.IsCacheEnabled = false; 22 | this.MaxCacheSize = 5000; 23 | this.IsMaxCacheSizeEnabled = false; 24 | } 25 | 26 | internal bool IsCacheEnabled { get; set; } 27 | 28 | internal bool IsMaxCacheSizeEnabled 29 | { 30 | get 31 | { 32 | return this.isMaxCacheSizeEnabled; 33 | } 34 | set 35 | { 36 | if (this.isMaxCacheSizeEnabled != value) 37 | { 38 | this.isMaxCacheSizeEnabled = value; 39 | this.ReduceCacheSize(this.MaxCacheSize); 40 | } 41 | } 42 | } 43 | 44 | internal int MaxCacheSize { get; set; } 45 | 46 | private void ReduceCacheSize(int newCacheSize) 47 | { 48 | lock (this.syncObj) 49 | { 50 | if (this.IsMaxCacheSizeEnabled && this.cache.Count >= this.MaxCacheSize) 51 | { 52 | // Take the top-weighted cache items according to ReadAccessCount 53 | this.cache = this.cache.OrderByDescending(x => x.Value.ReadAccessCount).Take(newCacheSize).ToDictionary(s => s.Key, s => s.Value); 54 | } 55 | } 56 | } 57 | 58 | internal void UpdateCache(Type sourceType, Type targetType, bool isConvertable, IConversionAttempt conversionAttempt) 59 | { 60 | if (this.IsCacheEnabled == false) 61 | { 62 | return; 63 | } 64 | 65 | lock (this.syncObj) 66 | { 67 | this.ReduceCacheSize(this.MaxCacheSize - 1); // -1 because we are about to insert a new item 68 | 69 | var key = new KeyValuePair(sourceType, targetType); 70 | var value = new WeightedCacheResult(isCached: true, isConvertable: isConvertable, conversionAttempt: conversionAttempt); 71 | 72 | this.cache[key] = value; 73 | } 74 | } 75 | 76 | /// 77 | /// Tries to get a CacheResult from given {sourceType, targetType} mapping. 78 | /// 79 | /// A CacheResult which indicates if and how the given {sourceType, targetType} mapping can be converted. 80 | internal CacheResult TryGetCachedValue(Type sourceType, Type targetType) 81 | { 82 | if (this.IsCacheEnabled == false) 83 | { 84 | return new CacheResult(isCached: false); 85 | } 86 | 87 | lock (this.syncObj) 88 | { 89 | WeightedCacheResult cacheResult = null; 90 | var key = new KeyValuePair(sourceType, targetType); 91 | this.cache.TryGetValue(key, out cacheResult); 92 | 93 | if (cacheResult != null) 94 | { 95 | cacheResult.ReadAccessCount++; 96 | return cacheResult; 97 | } 98 | 99 | return new CacheResult(isCached: false); 100 | } 101 | } 102 | 103 | public void Reset() 104 | { 105 | lock (this.syncObj) 106 | { 107 | this.cache.Clear(); 108 | } 109 | } 110 | 111 | private class WeightedCacheResult : CacheResult 112 | { 113 | public WeightedCacheResult(bool isCached) 114 | : base(isCached) 115 | { 116 | } 117 | 118 | public WeightedCacheResult(bool isCached, bool isConvertable, IConversionAttempt conversionAttempt) 119 | : base(isCached, isConvertable, conversionAttempt) 120 | { 121 | } 122 | 123 | public int ReadAccessCount { get; set; } 124 | } 125 | } 126 | } -------------------------------------------------------------------------------- /TypeConverter/Caching/CacheResult.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | 3 | using TypeConverter.Attempts; 4 | 5 | namespace TypeConverter.Caching 6 | { 7 | [DebuggerDisplay("IsCached = {IsCached}, IsConvertable = {IsConvertable}, ConversionAttempt = {ConversionAttempt}")] 8 | internal class CacheResult 9 | { 10 | public CacheResult(bool isCached) 11 | { 12 | this.IsCached = isCached; 13 | } 14 | 15 | public CacheResult(bool isCached, bool isConvertable, IConversionAttempt conversionAttempt) 16 | : this(isCached) 17 | { 18 | this.IsConvertable = isConvertable; 19 | this.ConversionAttempt = conversionAttempt; 20 | } 21 | 22 | internal bool IsCached { get; private set; } 23 | 24 | internal bool IsConvertable { get; private set; } 25 | 26 | internal IConversionAttempt ConversionAttempt { get; private set; } 27 | } 28 | } -------------------------------------------------------------------------------- /TypeConverter/CastFlag.cs: -------------------------------------------------------------------------------- 1 | namespace TypeConverter 2 | { 3 | internal enum CastFlag 4 | { 5 | Undefined = 0, 6 | Implicit = 1, 7 | Explicit = 2, 8 | } 9 | } -------------------------------------------------------------------------------- /TypeConverter/CastResult.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | 4 | namespace TypeConverter 5 | { 6 | [DebuggerDisplay("Value = {Value}, IsSuccessful = {IsSuccessful}, CastFlag = {CastFlag}")] 7 | internal class CastResult : ConversionResult 8 | { 9 | public CastResult(object value, CastFlag castFlag) 10 | : base(value) 11 | { 12 | this.CastFlag = castFlag; 13 | } 14 | 15 | public CastResult(Exception exception, CastFlag castFlag) 16 | : base(exception) 17 | { 18 | this.CastFlag = castFlag; 19 | } 20 | 21 | public CastFlag CastFlag { get; private set; } 22 | } 23 | } -------------------------------------------------------------------------------- /TypeConverter/ConversionResult.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | 4 | using Guards; 5 | 6 | namespace TypeConverter 7 | { 8 | [DebuggerDisplay("Value = {Value}, IsSuccessful = {IsSuccessful}")] 9 | internal class ConversionResult 10 | { 11 | public ConversionResult(object value) 12 | { 13 | this.Value = value; 14 | } 15 | 16 | public ConversionResult(Exception exception) 17 | { 18 | Guard.ArgumentNotNull(exception, nameof(exception)); 19 | 20 | this.Exception = exception; 21 | } 22 | 23 | public bool IsSuccessful 24 | { 25 | get 26 | { 27 | return this.Exception == null; 28 | } 29 | } 30 | 31 | public object Value { get; private set; } 32 | 33 | public Exception Exception { get; private set; } 34 | } 35 | } -------------------------------------------------------------------------------- /TypeConverter/ConverterRegistry.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Linq.Expressions; 5 | using System.Reflection; 6 | 7 | using Guards; 8 | 9 | using TypeConverter.Attempts; 10 | using TypeConverter.Caching; 11 | using TypeConverter.Exceptions; 12 | using TypeConverter.Extensions; 13 | 14 | namespace TypeConverter 15 | { 16 | public class ConverterRegistry : IConverterRegistry, IConverterCache 17 | { 18 | private readonly CacheManager cacheManager; 19 | private readonly IList conversionAttempts; 20 | private readonly CustomConvertAttempt customConversionAttempt; 21 | private readonly MapAttempt mapAttempt; 22 | 23 | public ConverterRegistry() 24 | { 25 | this.customConversionAttempt = new CustomConvertAttempt(); 26 | this.mapAttempt = new MapAttempt(); 27 | this.conversionAttempts = new List 28 | { 29 | this.customConversionAttempt, 30 | this.mapAttempt, 31 | new CastAttempt(), 32 | new ChangeTypeAttempt(), 33 | new EnumParseAttempt(), 34 | new StringParseAttempt(), 35 | }; 36 | 37 | this.cacheManager = new CacheManager(); 38 | this.cacheManager.IsCacheEnabled = true; 39 | } 40 | 41 | /// 42 | public void RegisterConverter(IConvertable converter) 43 | { 44 | Guard.ArgumentNotNull(converter, nameof(converter)); 45 | 46 | this.customConversionAttempt.RegisterConverter(converter); 47 | } 48 | 49 | /// 50 | public void RegisterConverter(Func> converterFactory) 51 | { 52 | Guard.ArgumentNotNull(converterFactory, nameof(converterFactory)); 53 | 54 | this.customConversionAttempt.RegisterConverter(converterFactory); 55 | } 56 | 57 | public void RegisterMapping(Expression> destinationMember, Expression> sourceMember) 58 | { 59 | Guard.ArgumentNotNull(destinationMember, nameof(destinationMember)); 60 | Guard.ArgumentNotNull(sourceMember, nameof(sourceMember)); 61 | 62 | this.mapAttempt.RegisterMapping(destinationMember, sourceMember); 63 | } 64 | 65 | /// 66 | public void RegisterConverter(Type converterType) 67 | { 68 | this.RegisterConverter(() => this.CreateConverterInstance(converterType)); 69 | } 70 | 71 | private IConvertable CreateConverterInstance(Type converterType) 72 | { 73 | Guard.ArgumentNotNull(converterType, nameof(converterType)); 74 | 75 | if (typeof(IConvertable).GetTypeInfo().IsAssignableFrom(converterType.GetTypeInfo())) 76 | { 77 | return (IConvertable)Activator.CreateInstance(converterType); 78 | } 79 | 80 | return null; 81 | } 82 | 83 | /// 84 | public TTarget Convert(object value) 85 | { 86 | Guard.ArgumentNotNull(value, nameof(value)); 87 | 88 | return (TTarget)this.ConvertInternal(value.GetType(), typeof(TTarget), value); 89 | } 90 | 91 | /// 92 | public TTarget TryConvert(object value, TTarget defaultReturnValue = default(TTarget)) 93 | { 94 | if (value == null) 95 | { 96 | return defaultReturnValue; 97 | } 98 | 99 | return (TTarget)this.ConvertInternal(value.GetType(), typeof(TTarget), value, defaultReturnValue, throwIfConvertFails: false); 100 | } 101 | 102 | /// 103 | public TTarget Convert(TSource value) 104 | { 105 | return (TTarget)this.ConvertInternal(typeof(TSource), typeof(TTarget), value); 106 | } 107 | 108 | /// 109 | public TTarget TryConvert(TSource value, TTarget defaultReturnValue = default(TTarget)) 110 | { 111 | return (TTarget)this.ConvertInternal(typeof(TSource), typeof(TTarget), value, defaultReturnValue, throwIfConvertFails: false); 112 | } 113 | 114 | /// 115 | public object Convert(Type targetType, TSource value) 116 | { 117 | return this.ConvertInternal(typeof(TSource), targetType, value); 118 | } 119 | 120 | /// 121 | public object TryConvert(Type targetType, TSource value, object defaultReturnValue) 122 | { 123 | return this.ConvertInternal(typeof(TSource), targetType, value, defaultReturnValue, throwIfConvertFails: false); 124 | } 125 | 126 | /// 127 | public object Convert(Type sourceType, Type targetType, object value) 128 | { 129 | return this.ConvertInternal(sourceType, targetType, value); 130 | } 131 | 132 | /// 133 | public object TryConvert(Type sourceType, Type targetType, object value, object defaultReturnValue) 134 | { 135 | return this.ConvertInternal(sourceType, targetType, value, defaultReturnValue, throwIfConvertFails: false); 136 | } 137 | 138 | private object ConvertInternal(Type sourceType, Type targetType, object value, object defaultReturnValue = null, bool throwIfConvertFails = true) 139 | { 140 | Guard.ArgumentNotNull(value, nameof(value)); 141 | Guard.ArgumentNotNull(sourceType, nameof(sourceType)); 142 | Guard.ArgumentNotNull(targetType, nameof(targetType)); 143 | 144 | // If source type is the same as target type, no conversion/casting is necessary 145 | if (sourceType == targetType) 146 | { 147 | return value; 148 | } 149 | 150 | // Try to read conversion method from cache 151 | var cachedValue = this.TryGetCachedValue(value, sourceType, targetType); 152 | if (cachedValue != null && cachedValue.IsSuccessful) 153 | { 154 | return cachedValue.Value; 155 | } 156 | 157 | // Try to convert using defined sequence of conversion attempts 158 | foreach (var conversionAttempt in this.conversionAttempts) 159 | { 160 | var conversionResult = conversionAttempt.TryConvert(value, sourceType, targetType); 161 | if (conversionResult != null && conversionResult.IsSuccessful) 162 | { 163 | this.cacheManager.UpdateCache( 164 | sourceType: sourceType, 165 | targetType: targetType, 166 | isConvertable: conversionResult.IsSuccessful, 167 | conversionAttempt: conversionAttempt); 168 | 169 | return conversionResult.Value; 170 | } 171 | } 172 | 173 | // If all fails, we either throw an exception 174 | if (throwIfConvertFails) 175 | { 176 | throw ConversionNotSupportedException.Create(sourceType, targetType); 177 | } 178 | 179 | // ...or return a default target value 180 | if (defaultReturnValue == null) 181 | { 182 | return targetType.GetDefault(); 183 | } 184 | 185 | return defaultReturnValue; 186 | } 187 | 188 | private ConversionResult TryGetCachedValue(object value, Type sourceType, Type targetType) 189 | { 190 | var cacheResult = this.cacheManager.TryGetCachedValue(sourceType, targetType); 191 | if (cacheResult.IsCached) 192 | { 193 | if (cacheResult.IsConvertable) 194 | { 195 | var cachedAttempt = this.conversionAttempts.Single(a => a == cacheResult.ConversionAttempt); 196 | var convertedValue = cachedAttempt.TryConvert(value, sourceType, targetType); 197 | return convertedValue; 198 | } 199 | 200 | return new ConversionResult(ConversionNotSupportedException.Create(sourceType, targetType)); 201 | } 202 | 203 | return null; 204 | } 205 | 206 | #region IConverterRegistry facade implementation 207 | 208 | /// 209 | void IConverterRegistry.Reset() 210 | { 211 | this.customConversionAttempt.Reset(); 212 | this.mapAttempt.Reset(); 213 | } 214 | 215 | /// 216 | bool IConverterCache.IsCacheEnabled 217 | { 218 | get 219 | { 220 | return this.cacheManager.IsCacheEnabled; 221 | } 222 | set 223 | { 224 | this.cacheManager.IsCacheEnabled = value; 225 | } 226 | } 227 | 228 | /// 229 | bool IConverterCache.IsMaxCacheSizeEnabled 230 | { 231 | get 232 | { 233 | return this.cacheManager.IsMaxCacheSizeEnabled; 234 | } 235 | set 236 | { 237 | this.cacheManager.IsMaxCacheSizeEnabled = value; 238 | } 239 | } 240 | 241 | /// 242 | public int MaxCacheSize 243 | { 244 | get 245 | { 246 | return this.cacheManager.MaxCacheSize; 247 | } 248 | set 249 | { 250 | this.cacheManager.MaxCacheSize = value; 251 | } 252 | } 253 | 254 | /// 255 | void IConverterCache.Reset() 256 | { 257 | this.cacheManager.Reset(); 258 | } 259 | 260 | #endregion 261 | } 262 | } -------------------------------------------------------------------------------- /TypeConverter/Converters/StringToBoolConverter.cs: -------------------------------------------------------------------------------- 1 | namespace TypeConverter.Converters 2 | { 3 | public class StringToBoolConverter : IConvertable, IConvertable 4 | { 5 | public bool Convert(string value) 6 | { 7 | return bool.Parse(value); 8 | } 9 | 10 | public string Convert(bool value) 11 | { 12 | return value.ToString(); 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /TypeConverter/Converters/StringToDateTimeConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | 4 | namespace TypeConverter.Converters 5 | { 6 | public class StringToDateTimeConverter : ToStringFormattableConvertable, IConvertable 7 | { 8 | protected override string Format { get { return "O"; } } // https://msdn.microsoft.com/en-us/library/az4se3k1(v=vs.110).aspx#Roundtrip 9 | 10 | public DateTime Convert(string value) 11 | { 12 | return DateTime.ParseExact(value, this.Format, CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind); 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /TypeConverter/Converters/StringToDateTimeOffsetConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | 4 | namespace TypeConverter.Converters 5 | { 6 | public class StringToDateTimeOffsetConverter : ToStringFormattableConvertable, IConvertable 7 | { 8 | protected override string Format { get { return "O"; } } // https://msdn.microsoft.com/en-us/library/az4se3k1(v=vs.110).aspx#Roundtrip 9 | 10 | public DateTimeOffset Convert(string value) 11 | { 12 | return DateTimeOffset.ParseExact(value, this.Format, CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind); 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /TypeConverter/Converters/StringToDecimalConverter.cs: -------------------------------------------------------------------------------- 1 | using System.Globalization; 2 | 3 | namespace TypeConverter.Converters 4 | { 5 | public class StringToDecimalConverter : ToStringFormattableConvertable, IConvertable 6 | { 7 | protected override string Format { get { return "G"; } } // https://msdn.microsoft.com/en-us/library/dwhawy9k(v=vs.110).aspx#GFormatString 8 | 9 | public decimal Convert(string value) 10 | { 11 | return decimal.Parse(value, CultureInfo.InvariantCulture); 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /TypeConverter/Converters/StringToDoubleConverter.cs: -------------------------------------------------------------------------------- 1 | using System.Globalization; 2 | 3 | namespace TypeConverter.Converters 4 | { 5 | public class StringToDoubleConverter : ToStringFormattableConvertable, IConvertable 6 | { 7 | protected override string Format { get { return "R"; } } // https://msdn.microsoft.com/en-us/library/dwhawy9k(v=vs.110).aspx#RFormatString 8 | 9 | public double Convert(string value) 10 | { 11 | return double.Parse(value, CultureInfo.InvariantCulture); 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /TypeConverter/Converters/StringToFloatConverter.cs: -------------------------------------------------------------------------------- 1 | using System.Globalization; 2 | 3 | namespace TypeConverter.Converters 4 | { 5 | public class StringToFloatConverter : ToStringFormattableConvertable, IConvertable 6 | { 7 | protected override string Format { get { return "R"; } } // https://msdn.microsoft.com/en-us/library/dwhawy9k(v=vs.110).aspx#RFormatString 8 | 9 | public float Convert(string value) 10 | { 11 | return float.Parse(value, CultureInfo.InvariantCulture); 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /TypeConverter/Converters/StringToGuidConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace TypeConverter.Converters 4 | { 5 | public class StringToGuidConverter : ToStringFormattableConvertable, IConvertable 6 | { 7 | protected override string Format { get { return "B"; } } // https://msdn.microsoft.com/de-de/library/s6tk2z69(v=vs.110).aspx 8 | 9 | public Guid Convert(string value) 10 | { 11 | return new Guid(value); 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /TypeConverter/Converters/StringToIntegerConverter.cs: -------------------------------------------------------------------------------- 1 | namespace TypeConverter.Converters 2 | { 3 | public class StringToIntegerConverter : IConvertable, IConvertable 4 | { 5 | public int Convert(string value) 6 | { 7 | return int.Parse(value); 8 | } 9 | 10 | public string Convert(int value) 11 | { 12 | return value.ToString(); 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /TypeConverter/Converters/StringToUriConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace TypeConverter.Converters 4 | { 5 | public class StringToUriConverter : IConvertable, IConvertable 6 | { 7 | public Uri Convert(string value) 8 | { 9 | return new Uri(value); 10 | } 11 | 12 | public string Convert(Uri value) 13 | { 14 | return value.AbsoluteUri; 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /TypeConverter/Converters/ToStringFormattableConvertable.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | 4 | namespace TypeConverter.Converters 5 | { 6 | public abstract class ToStringFormattableConvertable : IConvertable 7 | where TSource : IFormattable 8 | { 9 | protected abstract string Format { get; } 10 | 11 | public string Convert(TSource value) 12 | { 13 | return value.ToString(this.Format, CultureInfo.InvariantCulture); 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /TypeConverter/Exceptions/ConversionNotSupportedException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | using TypeConverter.Extensions; 4 | 5 | namespace TypeConverter.Exceptions 6 | { 7 | internal class ConversionNotSupportedException : Exception 8 | { 9 | private ConversionNotSupportedException(string message, Exception innerException) 10 | : base(message, innerException) 11 | { 12 | } 13 | 14 | private ConversionNotSupportedException(string message) 15 | : base(message) 16 | { 17 | } 18 | 19 | public static ConversionNotSupportedException Create(Type sourceType, Type targetType, Exception innerException = null) 20 | { 21 | var convertableInterfaceType = typeof(IConvertable); 22 | string exceptionMessage = string.Format( 23 | "Could not find {0}<{1}, {2}>. Use RegisterConverter method to register a converter which converts between type {1} and type {2}.", 24 | convertableInterfaceType.GetFormattedName(), 25 | sourceType.GetFormattedName(), 26 | targetType.GetFormattedName()); 27 | 28 | if (innerException == null) 29 | { 30 | return new ConversionNotSupportedException(exceptionMessage); 31 | } 32 | return new ConversionNotSupportedException(exceptionMessage, innerException); 33 | } 34 | 35 | public static ConversionNotSupportedException Create(Type sourceType, Type targetType, string message) 36 | { 37 | string exceptionMessage = string.Format("Could not convert between type {0} and {1}. " + message, sourceType.GetFormattedName(), targetType.GetFormattedName()); 38 | 39 | return new ConversionNotSupportedException(exceptionMessage); 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /TypeConverter/Extensions/ConverterRegistryExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace TypeConverter.Extensions 2 | { 3 | public static class ConverterRegistryExtensions 4 | { 5 | public static void RegisterConverters(this IConverterRegistry converterRegistry, params IConvertable[] converters) 6 | { 7 | foreach (var converter in converters) 8 | { 9 | converterRegistry.RegisterConverter(converter); 10 | } 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /TypeConverter/Extensions/TypeExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection; 5 | 6 | using Guards; 7 | 8 | namespace TypeConverter.Extensions 9 | { 10 | internal static class TypeExtensions 11 | { 12 | internal static IEnumerable GetDeclaredMethodsRecursively(this Type type) 13 | { 14 | return GetDeclaredMethodsRecursively(type.GetTypeInfo()); 15 | } 16 | 17 | internal static IEnumerable GetDeclaredMethodsRecursively(this TypeInfo typeInfo) 18 | { 19 | if (typeInfo == null) 20 | { 21 | return null; 22 | } 23 | 24 | var methods = GetDeclaredMethodsRecursively(typeInfo.AsType(), new List()); 25 | return methods; 26 | } 27 | 28 | private static IEnumerable GetDeclaredMethodsRecursively(this Type type, List methods) 29 | { 30 | if (type == null) 31 | { 32 | return methods; 33 | } 34 | 35 | var typeInfo = type.GetTypeInfo(); 36 | var temp = methods.ToList(); 37 | methods = new List(typeInfo.DeclaredMethods); 38 | methods.AddRange(temp); 39 | 40 | return GetDeclaredMethodsRecursively(typeInfo.BaseType, methods); 41 | } 42 | 43 | /// 44 | /// Determines whether the specified types are considered equal. 45 | /// 46 | /// A instance. 47 | /// A type possible derived from the parent type 48 | /// 49 | /// True, when an object instance of the type child 50 | /// can be used as an object of the type parent; otherwise, false. 51 | /// 52 | /// 53 | /// Note that nullable types does not have a parent-child relation to it's underlying type. 54 | /// For example, the 'int?' type (nullable int) and the 'int' type 55 | /// aren't a parent and it's child. 56 | /// 57 | internal static bool IsSameOrParent(this Type parent, Type child) 58 | { 59 | Guard.ArgumentNotNull(parent, nameof(parent)); 60 | Guard.ArgumentNotNull(child, nameof(child)); 61 | 62 | var parentTypeInfo = parent.GetTypeInfo(); 63 | var childTypeInfo = child.GetTypeInfo(); 64 | 65 | if (parent == child || 66 | parentTypeInfo.IsAssignableFrom(childTypeInfo) || 67 | childTypeInfo.IsEnum && Enum.GetUnderlyingType(child) == parent || 68 | childTypeInfo.IsSubclassOf(parent)) 69 | { 70 | return true; 71 | } 72 | 73 | if (parentTypeInfo.IsGenericTypeDefinition) 74 | { 75 | var objectTypeInfo = typeof(object).GetTypeInfo(); 76 | for (var t = childTypeInfo; t != objectTypeInfo && t != null; t = t.BaseType != null ? t.BaseType.GetTypeInfo() : null) 77 | { 78 | if (t.IsGenericType && t.GetGenericTypeDefinition() == parent) 79 | { 80 | return true; 81 | } 82 | } 83 | } 84 | 85 | if (parentTypeInfo.IsInterface) 86 | { 87 | var interfaces = childTypeInfo.ImplementedInterfaces.Select(i => i.GetTypeInfo()); 88 | 89 | foreach (var t in interfaces) 90 | { 91 | if (parentTypeInfo.IsGenericTypeDefinition) 92 | { 93 | if (t.IsGenericType && t.GetGenericTypeDefinition() == parent) 94 | { 95 | return true; 96 | } 97 | } 98 | else if (t == parentTypeInfo) 99 | { 100 | return true; 101 | } 102 | } 103 | } 104 | 105 | return false; 106 | } 107 | 108 | internal static object GetDefault(this Type type) 109 | { 110 | if (type.GetTypeInfo().IsValueType) 111 | { 112 | return Activator.CreateInstance(type); 113 | } 114 | 115 | return null; 116 | } 117 | 118 | internal static string GetFormattedName(this Type type) 119 | { 120 | Guard.ArgumentNotNull(type, nameof(type)); 121 | 122 | var typeInfo = type.GetTypeInfo(); 123 | if (!typeInfo.IsGenericType) 124 | { 125 | return type.Name; 126 | } 127 | 128 | return string.Format("{0}<{1}>", type.Name.Substring(0, type.Name.IndexOf('`')), string.Join(", ", typeInfo.GenericTypeArguments.Select(t => t.GetFormattedName()))); 129 | } 130 | 131 | internal static string GetFormattedFullname(this Type type) 132 | { 133 | Guard.ArgumentNotNull(type, nameof(type)); 134 | 135 | var typeInfo = type.GetTypeInfo(); 136 | if (!typeInfo.IsGenericType) 137 | { 138 | return type.ToString(); 139 | } 140 | 141 | return string.Format("{0}.{1}<{2}>", type.Namespace, type.Name.Substring(0, type.Name.IndexOf('`')), string.Join(", ", typeInfo.GenericTypeArguments.Select(t => t.GetFormattedFullname()))); 142 | } 143 | } 144 | } -------------------------------------------------------------------------------- /TypeConverter/IConvertable.cs: -------------------------------------------------------------------------------- 1 | namespace TypeConverter 2 | { 3 | public interface IConvertable : IConvertable 4 | { 5 | ////bool CanConvert(TSource value, TTarget target); 6 | 7 | /// 8 | /// Converts the given value of type TSource into an object of type TTarget. 9 | /// 10 | /// The source value to be converted. 11 | TTarget Convert(TSource value); 12 | } 13 | 14 | public interface IConvertable 15 | { 16 | } 17 | } -------------------------------------------------------------------------------- /TypeConverter/IConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace TypeConverter 4 | { 5 | /// 6 | /// IConverter interface allows to perform conversion operations 7 | /// using all configured conversion strategies. 8 | /// Use to configure conversion strategies. 9 | /// 10 | public interface IConverter 11 | { 12 | /// 13 | /// Converts the given into an object of type . 14 | /// 15 | /// Generic target type. 16 | /// The source value to be converted. 17 | TTarget Convert(object value); 18 | 19 | /// 20 | /// Converts the given into an object of type . 21 | /// 22 | /// Generic source type. 23 | /// Generic target type. 24 | /// The source value to be converted. 25 | TTarget Convert(TSource value); 26 | 27 | /// 28 | /// Converts the given into an object of type . 29 | /// 30 | /// The target type. 31 | /// Generic source type. 32 | /// The source value to be converted. 33 | object Convert(Type targetType, TSource value); 34 | 35 | /// 36 | /// Converts the given into an object of type . 37 | /// 38 | /// The source type. 39 | /// The target type. 40 | /// The source value to be converted. 41 | object Convert(Type sourceType, Type targetType, object value); 42 | 43 | /// 44 | /// Tries to convert the given into an object of type . 45 | /// If a conversion is not possible, it returns the given . 46 | /// 47 | /// Generic target type. 48 | /// The source value to be converted. 49 | /// The default return value if the conversion failed. 50 | TTarget TryConvert(object value, TTarget defaultReturnValue); 51 | 52 | /// 53 | /// Tries to convert the given into an object of type . 54 | /// 55 | /// Generic source type. 56 | /// Generic target type. 57 | /// The source value to be converted. 58 | /// The default return value if the conversion failed. 59 | TTarget TryConvert(TSource value, TTarget defaultReturnValue); 60 | 61 | /// 62 | /// Tries to convert the given into an object of given . 63 | /// 64 | /// Generic source type. 65 | /// The target type. 66 | /// The source value to be converted. 67 | /// The default return value if the conversion failed. 68 | object TryConvert(Type targetType, TSource value, object defaultReturnValue); 69 | 70 | /// 71 | /// Tries to convert the given into an object of given . 72 | /// 73 | /// The source type. 74 | /// The target type. 75 | /// The source value to be converted. 76 | /// The default return value if the conversion failed. 77 | object TryConvert(Type sourceType, Type targetType, object value, object defaultReturnValue); 78 | } 79 | } -------------------------------------------------------------------------------- /TypeConverter/IConverterCache.cs: -------------------------------------------------------------------------------- 1 | namespace TypeConverter 2 | { 3 | public interface IConverterCache 4 | { 5 | /// 6 | /// Enables or disables the caching functionality. 7 | /// 8 | bool IsCacheEnabled { get; set; } 9 | 10 | /// 11 | /// Enables or disables to cache size limit. 12 | /// 13 | bool IsMaxCacheSizeEnabled { get; set; } 14 | 15 | /// 16 | /// Gets or sets the cache size limit. 17 | /// 18 | int MaxCacheSize { get; set; } 19 | 20 | /// 21 | /// Flushes the cache. 22 | /// 23 | void Reset(); 24 | } 25 | } -------------------------------------------------------------------------------- /TypeConverter/IConverterRegistry.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | 4 | namespace TypeConverter 5 | { 6 | /// 7 | /// The ConverterRegistry is the entrypoint of TypeConverter which allows to register converters 8 | /// and convert values of a given source type to an expected target type. 9 | /// 10 | public interface IConverterRegistry : IConverter 11 | { 12 | /// 13 | /// Registers a converter which converts between certain source and target types . 14 | /// 15 | /// The converter which is used to convert beween source and target type. 16 | void RegisterConverter(IConvertable converter); 17 | 18 | /// 19 | /// Registers a converter factory which converts between generic types and . 20 | /// 21 | /// Generic source type. 22 | /// Generic target type. 23 | /// The factory which creates the converter to convert between source and target type. 24 | void RegisterConverter(Func> converterFactory); 25 | 26 | void RegisterMapping(Expression> destinationMember, Expression> sourceMember); 27 | 28 | /// 29 | /// Registers a converter (as a type) which converts between generic types and . 30 | /// 31 | /// Generic source type. 32 | /// Generic target type. 33 | /// 34 | /// The converter type which will be instanciated and used to convert between source and target 35 | /// type. 36 | /// 37 | void RegisterConverter(Type converterType); 38 | 39 | /// 40 | /// Clears all registered IConverters 41 | /// and purges the cache. 42 | /// 43 | void Reset(); 44 | } 45 | } -------------------------------------------------------------------------------- /TypeConverter/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Resources; 3 | using System.Runtime.CompilerServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | 9 | [assembly: AssemblyTitle("TypeConverter")] 10 | [assembly: AssemblyDescription("")] 11 | [assembly: AssemblyConfiguration("")] 12 | [assembly: AssemblyCompany("Thomas Galliker")] 13 | [assembly: AssemblyProduct("TypeConverter")] 14 | [assembly: AssemblyCopyright("Copyright © 2017")] 15 | [assembly: AssemblyTrademark("")] 16 | [assembly: AssemblyCulture("")] 17 | [assembly: NeutralResourcesLanguage("en")] 18 | 19 | // Version information for an assembly consists of the following four values: 20 | // 21 | // Major Version 22 | // Minor Version 23 | // Build Number 24 | // Revision 25 | // 26 | // You can specify all the values or you can default the Build and Revision Numbers 27 | // by using the '*' as shown below: 28 | // [assembly: AssemblyVersion("1.0.28")] 29 | 30 | [assembly: AssemblyVersion("1.0.28")] 31 | [assembly: AssemblyFileVersion("1.0.28-pre92")] 32 | [assembly: 33 | InternalsVisibleTo( 34 | "TypeConverter.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f1aadb1821f090" 35 | + "cd1d3ece956dd3e884762f9c9f2fdeb1684ec9b4689d6d4a66fb715ee2a40f2f3479b6aa4e60d2" + "69b4539aebdec9412ce583d7c09661ec2084a5da465ee3a5d3e1901ada0ac9e877f7403011efd2" 36 | + "9be468378b136873cc8a700f68d0672eee60a4626f5c80618c61d9a63f9cfcbcadb161653294ad" + "fa282bd7")] 37 | -------------------------------------------------------------------------------- /TypeConverter/TypeConverter.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 10.0 6 | Debug 7 | AnyCPU 8 | {A6FCEF44-D2BA-42C7-B3CB-13667BCD7B54} 9 | Library 10 | Properties 11 | TypeConverter 12 | TypeConverter 13 | en-US 14 | 512 15 | {786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 16 | Profile259 17 | v4.5 18 | 19 | 20 | true 21 | full 22 | false 23 | bin\Debug\ 24 | TRACE;DEBUG;PORTABLE 25 | prompt 26 | 4 27 | 28 | 29 | pdbonly 30 | true 31 | bin\Release\ 32 | TRACE;PORTABLE 33 | prompt 34 | 4 35 | bin\Release\TypeConverter.xml 36 | 37 | 38 | true 39 | 40 | 41 | TypeConverterKey.snk 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | ..\packages\Guards.1.0.13\lib\portable-net45+dnxcore50+wp80+wpa81+win81+netcore45+monoandroid10+monotouch10+xamarin.ios10\Guards.dll 85 | True 86 | 87 | 88 | 89 | 96 | -------------------------------------------------------------------------------- /TypeConverter/TypeConverterKey.snk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thomasgalliker/TypeConverter/565afd5a018221a39bbf24e517f87cce683feaff/TypeConverter/TypeConverterKey.snk -------------------------------------------------------------------------------- /TypeConverter/TypeConverterPublicKey.snk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thomasgalliker/TypeConverter/565afd5a018221a39bbf24e517f87cce683feaff/TypeConverter/TypeConverterPublicKey.snk -------------------------------------------------------------------------------- /TypeConverter/Utils/ReflectionHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | using System.Reflection; 4 | 5 | namespace TypeConverter.Utils 6 | { 7 | internal static class ReflectionHelper 8 | { 9 | internal static MethodInfo GetMethod(Expression> expression) 10 | { 11 | MethodCallExpression callExpression = (MethodCallExpression)expression.Body; 12 | return callExpression.Method; 13 | } 14 | 15 | public static PropertyInfo GetPropertyInfo(Expression> expression) 16 | { 17 | Type type = typeof(TSource); 18 | 19 | 20 | 21 | var member = expression.Body as MemberExpression; 22 | if (member == null) 23 | { 24 | if (expression.Body.NodeType == ExpressionType.Convert) 25 | { 26 | var body = (UnaryExpression)expression.Body; 27 | member = body.Operand as MemberExpression; 28 | } 29 | 30 | if (member == null) 31 | { 32 | throw new ArgumentException($"Expression '{expression.ToString()}' refers to a method, not a property."); 33 | } 34 | } 35 | 36 | 37 | 38 | PropertyInfo propInfo = member.Member as PropertyInfo; 39 | if (propInfo == null) 40 | throw new ArgumentException($"Expression '{expression.ToString()}' refers to a field, not a property."); 41 | 42 | if (type != propInfo.DeclaringType && 43 | !type.GetTypeInfo().IsSubclassOf(propInfo.DeclaringType)) 44 | throw new ArgumentException($"Expresion '{expression.ToString()}' refers to a property that is not from type {type}."); 45 | 46 | return propInfo; 47 | } 48 | private static bool IsIndexedPropertyAccess(Expression expression) 49 | { 50 | return IsMethodExpression(expression) && expression.ToString().Contains("get_Item"); 51 | } 52 | 53 | private static bool IsMethodExpression(Expression expression) 54 | { 55 | return expression is MethodCallExpression || (expression is UnaryExpression && IsMethodExpression((expression as UnaryExpression).Operand)); 56 | } 57 | 58 | ////private static Member GetMember(Expression expression) 59 | ////{ 60 | //// if (IsIndexedPropertyAccess(expression)) 61 | //// return GetDynamicComponentProperty(expression).ToMember(); 62 | //// if (IsMethodExpression(expression)) 63 | //// return ((MethodCallExpression)expression).Method.ToMember(); 64 | 65 | //// var memberExpression = GetMemberExpression(expression); 66 | 67 | //// return memberExpression.Member.ToMember(); 68 | ////} 69 | 70 | ////private static PropertyInfo GetDynamicComponentProperty(Expression expression) 71 | ////{ 72 | //// Type desiredConversionType = null; 73 | //// MethodCallExpression methodCallExpression = null; 74 | //// var nextOperand = expression; 75 | 76 | //// while (nextOperand != null) 77 | //// { 78 | //// if (nextOperand.NodeType == ExpressionType.Call) 79 | //// { 80 | //// methodCallExpression = nextOperand as MethodCallExpression; 81 | //// desiredConversionType = desiredConversionType ?? methodCallExpression.Method.ReturnType; 82 | //// break; 83 | //// } 84 | 85 | //// if (nextOperand.NodeType != ExpressionType.Convert) 86 | //// throw new ArgumentException("Expression not supported", "expression"); 87 | 88 | //// var unaryExpression = (UnaryExpression)nextOperand; 89 | //// desiredConversionType = unaryExpression.Type; 90 | //// nextOperand = unaryExpression.Operand; 91 | //// } 92 | 93 | //// var constExpression = methodCallExpression.Arguments[0] as ConstantExpression; 94 | 95 | //// return new DummyPropertyInfo((string)constExpression.Value, desiredConversionType); 96 | ////} 97 | 98 | public static MemberExpression GetMemberExpression(Expression expression) 99 | { 100 | return GetMemberExpression(expression, true); 101 | } 102 | 103 | public static MemberExpression GetMemberExpression(Expression expression, bool enforceCheck) 104 | { 105 | MemberExpression memberExpression = null; 106 | if (expression.NodeType == ExpressionType.Convert) 107 | { 108 | var body = (UnaryExpression)expression; 109 | memberExpression = body.Operand as MemberExpression; 110 | } 111 | else if (expression.NodeType == ExpressionType.MemberAccess) 112 | { 113 | memberExpression = expression as MemberExpression; 114 | } 115 | 116 | if (enforceCheck && memberExpression == null) 117 | { 118 | throw new ArgumentException("Not a member access", "expression"); 119 | } 120 | 121 | return memberExpression; 122 | } 123 | } 124 | } -------------------------------------------------------------------------------- /TypeConverter/Utils/TypeHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Linq.Expressions; 5 | using System.Reflection; 6 | using System.Runtime.CompilerServices; 7 | 8 | using Guards; 9 | 10 | using Microsoft.CSharp.RuntimeBinder; 11 | 12 | using TypeConverter.Exceptions; 13 | using TypeConverter.Extensions; 14 | 15 | using CSharpBinder = Microsoft.CSharp.RuntimeBinder.Binder; 16 | 17 | namespace TypeConverter.Utils 18 | { 19 | internal static class TypeHelper 20 | { 21 | internal static CastResult CastTo(object value, Type targetType) 22 | { 23 | if (value == null) 24 | { 25 | return new CastResult((object)null, CastFlag.Undefined); 26 | } 27 | 28 | Guard.ArgumentNotNull(targetType, nameof(targetType)); 29 | 30 | var sourceType = value.GetType(); 31 | var sourceTypeInfo = value.GetType().GetTypeInfo(); 32 | var targetTypeInfo = targetType.GetTypeInfo(); 33 | 34 | if (targetTypeInfo.IsGenericType && !targetTypeInfo.GenericTypeArguments.Any()) 35 | { 36 | return 37 | new CastResult( 38 | ConversionNotSupportedException.Create( 39 | sourceType, 40 | targetType, 41 | string.Format("The target type {0} does not have sufficient generic type arguments specified.", targetType.GetFormattedName())), 42 | CastFlag.Undefined); 43 | } 44 | 45 | CastResult castResult = null; 46 | CastFlag castFlag = CastFlag.Undefined; 47 | 48 | try 49 | { 50 | // explicit conversion always works if to : from OR if there's an implicit conversion 51 | if (targetType.IsSameOrParent(sourceType)) 52 | { 53 | castFlag = CastFlag.Implicit; 54 | var castedValue = GenericCast(() => AttemptImplicitCast(null), sourceType, targetType, value); 55 | castResult = new CastResult(castedValue, castFlag); 56 | } 57 | // for nullable types, we can simply strip off the nullability and evaluate the underyling types 58 | else if (Nullable.GetUnderlyingType(targetType) != null) 59 | { 60 | castResult = CastTo(value, Nullable.GetUnderlyingType(targetType)); 61 | } 62 | else if (sourceTypeInfo.IsValueType) 63 | { 64 | castFlag = CastFlag.Explicit; 65 | var castedValue = GenericCast(() => AttemptExplicitCast(null), sourceType, targetType, value); 66 | castResult = new CastResult(castedValue, castFlag); 67 | } 68 | else 69 | { 70 | // Implicit cast operators have priority in favour of explicit operators 71 | // since they should not lose precision. See C# language specification: 72 | // https://msdn.microsoft.com/en-us/library/z5z9kes2.aspx 73 | var conversionMethods = 74 | GetCastOperatorMethods(sourceType, targetType) 75 | .OrderByDescending(m => m.Name == "op_Implicit") 76 | .ThenByDescending(m => m.ReturnType == targetType || m.ReturnType.GetTypeInfo().IsAssignableFrom(targetTypeInfo)); 77 | 78 | foreach (var conversionMethod in conversionMethods) 79 | { 80 | try 81 | { 82 | var convertedValue = conversionMethod.Invoke(null, new[] { value }); 83 | castResult = CastTo(convertedValue, targetType); 84 | if (castResult.IsSuccessful) 85 | { 86 | break; 87 | } 88 | else 89 | { 90 | castResult = null; 91 | } 92 | } 93 | catch 94 | { 95 | } 96 | } 97 | } 98 | } 99 | catch (Exception ex) 100 | { 101 | castResult = new CastResult(ConversionNotSupportedException.Create(sourceType, targetType, ex.InnerException), castFlag); 102 | } 103 | 104 | if (castResult == null) 105 | { 106 | castResult = new CastResult(ConversionNotSupportedException.Create(sourceType, targetType), castFlag); 107 | } 108 | 109 | return castResult; 110 | } 111 | 112 | /// 113 | /// This methods returns a list of cast operation methods (implicit as well as explicit operators). 114 | /// 115 | private static IEnumerable GetCastOperatorMethods(Type sourceType, Type targetType) 116 | { 117 | var methodsOfSourceType = sourceType.GetDeclaredMethodsRecursively(); 118 | var methodsOfTargetType = targetType.GetDeclaredMethodsRecursively(); 119 | 120 | foreach (var mi in methodsOfSourceType.Concat(methodsOfTargetType)) 121 | { 122 | if (mi.IsSpecialName && (mi.Name == "op_Implicit" || mi.Name == "op_Explicit")) 123 | { 124 | var parameters = mi.GetParameters(); 125 | 126 | if (parameters.Length == 1 127 | && (parameters[0].ParameterType.GetTypeInfo().IsAssignableFrom(sourceType.GetTypeInfo()) || sourceType.GetTypeInfo().IsAssignableFrom(parameters[0].ParameterType.GetTypeInfo()))) 128 | { 129 | yield return mi; 130 | } 131 | } 132 | } 133 | } 134 | 135 | private static object GenericCast(Expression> expression, Type sourceType, Type targetType, object value) 136 | { 137 | var genericCastMethodDefinition = ReflectionHelper.GetMethod(expression).GetGenericMethodDefinition(); 138 | var genericCastMethod = genericCastMethodDefinition.MakeGenericMethod(sourceType, targetType); 139 | var castedValue = genericCastMethod.Invoke(null, new[] { value }); 140 | 141 | return castedValue; 142 | } 143 | 144 | private static TTo AttemptExplicitCast(TFrom value) 145 | { 146 | // based on the IL generated from 147 | //var x = (TTo)(dynamic)value; 148 | 149 | var binder = CSharpBinder.Convert(CSharpBinderFlags.ConvertExplicit, typeof(TTo), typeof(TypeHelper)); 150 | var callSite = CallSite>.Create(binder); 151 | //dynamic tDynCallSite = callSite; 152 | return callSite.Target(callSite, value); 153 | } 154 | 155 | private static TTo AttemptImplicitCast(TFrom value = default(TFrom)) 156 | { 157 | // based on the IL produced by: 158 | // dynamic list = new List(); 159 | // list.Add(value); 160 | // We can't use the above code because it will mimic a cast in a generic method 161 | // which doesn't have the same semantics as a cast in a non-generic method 162 | 163 | var list = new List(capacity: 1); 164 | var binder = CSharpBinder.InvokeMember( 165 | flags: CSharpBinderFlags.ResultDiscarded, 166 | name: "Add", 167 | typeArguments: null, 168 | context: typeof(TypeHelper), 169 | argumentInfo: 170 | new[] { CSharpArgumentInfo.Create(flags: CSharpArgumentInfoFlags.None, name: null), CSharpArgumentInfo.Create(flags: CSharpArgumentInfoFlags.UseCompileTimeType, name: null), }); 171 | var callSite = CallSite>.Create(binder); 172 | callSite.Target.Invoke(callSite, list, value); 173 | 174 | return list[0]; 175 | } 176 | } 177 | } -------------------------------------------------------------------------------- /TypeConverter/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | --------------------------------------------------------------------------------