Skip to content

Commit

Permalink
Added nullable to FastDynamicObject.
Browse files Browse the repository at this point in the history
  • Loading branch information
JoshClose committed Jun 21, 2024
1 parent 7dbb3fe commit f9e5fa4
Show file tree
Hide file tree
Showing 2 changed files with 115 additions and 116 deletions.
43 changes: 21 additions & 22 deletions src/CsvHelper/FastDynamicObject.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,22 @@
// See LICENSE.txt for details or visit http://www.opensource.org/licenses/ms-pl.html for MS-PL and http://opensource.org/licenses/Apache-2.0 for Apache 2.0.
// https://github.com/JoshClose/CsvHelper
using System.Collections;
using System.Diagnostics.CodeAnalysis;
using System.Dynamic;
using System.Linq.Expressions;
using System.Reflection;

namespace CsvHelper;

internal class FastDynamicObject : IDynamicMetaObjectProvider, IDictionary<string, object>
internal class FastDynamicObject : IDynamicMetaObjectProvider, IDictionary<string, object?>
{
private readonly Dictionary<string, object> dict;
private readonly Dictionary<string, object?> dict;

public FastDynamicObject()
{
dict = new Dictionary<string, object>();
dict = new Dictionary<string, object?>();
}

object IDictionary<string, object>.this[string key]
object? IDictionary<string, object?>.this[string key]
{
get
{
Expand All @@ -37,15 +36,15 @@ object IDictionary<string, object>.this[string key]
}
}

ICollection<string> IDictionary<string, object>.Keys => dict.Keys;
ICollection<string> IDictionary<string, object?>.Keys => dict.Keys;

ICollection<object> IDictionary<string, object>.Values => dict.Values;
ICollection<object?> IDictionary<string, object?>.Values => dict.Values;

int ICollection<KeyValuePair<string, object>>.Count => dict.Count;
int ICollection<KeyValuePair<string, object?>>.Count => dict.Count;

bool ICollection<KeyValuePair<string, object>>.IsReadOnly => false;
bool ICollection<KeyValuePair<string, object?>>.IsReadOnly => false;

object SetValue(string key, object value)
object? SetValue(string key, object? value)
{
dict[key] = value;

Expand All @@ -57,32 +56,32 @@ DynamicMetaObject IDynamicMetaObjectProvider.GetMetaObject(Expression parameter)
return new FastDynamicMetaObject(parameter, BindingRestrictions.Empty, this);
}

void IDictionary<string, object>.Add(string key, object value)
void IDictionary<string, object?>.Add(string key, object? value)
{
SetValue(key, value);
}

void ICollection<KeyValuePair<string, object>>.Add(KeyValuePair<string, object> item)
void ICollection<KeyValuePair<string, object?>>.Add(KeyValuePair<string, object?> item)
{
SetValue(item.Key, item.Value);
}

void ICollection<KeyValuePair<string, object>>.Clear()
void ICollection<KeyValuePair<string, object?>>.Clear()
{
dict.Clear();
}

bool ICollection<KeyValuePair<string, object>>.Contains(KeyValuePair<string, object> item)
bool ICollection<KeyValuePair<string, object?>>.Contains(KeyValuePair<string, object?> item)
{
return dict.Contains(item);
}

bool IDictionary<string, object>.ContainsKey(string key)
bool IDictionary<string, object?>.ContainsKey(string key)
{
return dict.ContainsKey(key);
}

void ICollection<KeyValuePair<string, object>>.CopyTo(KeyValuePair<string, object>[] array, int arrayIndex)
void ICollection<KeyValuePair<string, object?>>.CopyTo(KeyValuePair<string, object?>[] array, int arrayIndex)
{
if (arrayIndex < 0 || arrayIndex >= array.Length)
{
Expand All @@ -102,7 +101,7 @@ void ICollection<KeyValuePair<string, object>>.CopyTo(KeyValuePair<string, objec
}
}

IEnumerator<KeyValuePair<string, object>> IEnumerable<KeyValuePair<string, object>>.GetEnumerator()
IEnumerator<KeyValuePair<string, object?>> IEnumerable<KeyValuePair<string, object?>>.GetEnumerator()
{
return dict.GetEnumerator();
}
Expand All @@ -112,24 +111,24 @@ IEnumerator IEnumerable.GetEnumerator()
return dict.GetEnumerator();
}

bool IDictionary<string, object>.Remove(string key)
bool IDictionary<string, object?>.Remove(string key)
{
return dict.Remove(key);
}

bool ICollection<KeyValuePair<string, object>>.Remove(KeyValuePair<string, object> item)
bool ICollection<KeyValuePair<string, object?>>.Remove(KeyValuePair<string, object?> item)
{
return dict.Remove(item.Key);
}

bool IDictionary<string, object>.TryGetValue(string key, out object value)
bool IDictionary<string, object?>.TryGetValue(string key, out object? value)
{
return dict.TryGetValue(key, out value!);
}

private class FastDynamicMetaObject : DynamicMetaObject
{
private static readonly MethodInfo getValueMethod = typeof(IDictionary<string, object>).GetProperty("Item")!.GetGetMethod()!;
private static readonly MethodInfo getValueMethod = typeof(IDictionary<string, object?>).GetProperty("Item")!.GetGetMethod()!;
private static readonly MethodInfo setValueMethod = typeof(FastDynamicObject).GetMethod("SetValue", BindingFlags.NonPublic | BindingFlags.Instance)!;

public FastDynamicMetaObject(Expression expression, BindingRestrictions restrictions) : base(expression, restrictions) { }
Expand Down Expand Up @@ -165,7 +164,7 @@ public override DynamicMetaObject BindInvokeMember(InvokeMemberBinder binder, Dy

public override IEnumerable<string> GetDynamicMemberNames()
{
if (HasValue && Value is IDictionary<string, object> lookup)
if (HasValue && Value is IDictionary<string, object?> lookup)
{
return lookup.Keys;
}
Expand Down
188 changes: 94 additions & 94 deletions tests/CsvHelper.Tests/Dynamic/FastDynamicTests.cs
Original file line number Diff line number Diff line change
@@ -1,99 +1,99 @@
using System;
using System.Collections.Generic;
using Xunit;
using Xunit;

namespace CsvHelper.Tests.Dynamic
namespace CsvHelper.Tests.Dynamic;

public class FastDynamicObjectTests
{
public class FastDynamicObjectTests
[Fact]
public void Dynamic_SetAndGet_Works()
{
dynamic obj = new FastDynamicObject();
obj.Id = 1;
obj.Name = "one";
obj.Null = null;

var id = obj.Id;
var name = obj.Name;
var @null = obj.Null;

Assert.Equal(1, id);
Assert.Equal("one", name);
Assert.Null(@null);
}

[Fact]
public void CopyTo_NegativeIndex_Throws()
{
IDictionary<string, object?> d = new FastDynamicObject();
var a = new KeyValuePair<string, object?>[1];

Assert.Throws<ArgumentOutOfRangeException>(() => d.CopyTo(a, -1));
}

[Fact]
public void CopyTo_IndexLargerThanArrayLength_Throws()
{
IDictionary<string, object?> d = new FastDynamicObject();
var a = new KeyValuePair<string, object?>[1];

Assert.Throws<ArgumentOutOfRangeException>(() => d.CopyTo(a, a.Length));
}

[Fact]
public void CopyTo_SourceIsLargerThanDestination_Throws()
{
IDictionary<string, object?> d = new FastDynamicObject();
d["a"] = 1;
d["b"] = 2;
var a = new KeyValuePair<string, object?>[1];

Assert.Throws<ArgumentException>(() => d.CopyTo(a, 0));
}

[Fact]
public void CopyTo_IndexGreaterThanZeroAndSourceIsLargerThanDestination_Throws()
{
IDictionary<string, object?> d = new FastDynamicObject();
d["a"] = 1;
d["b"] = 2;
var a = new KeyValuePair<string, object?>[2];

Assert.Throws<ArgumentException>(() => d.CopyTo(a, 1));
}

[Fact]
public void CopyTo_StartAtZero_Copies()
{
IDictionary<string, object?> d = new FastDynamicObject();
d["a"] = 1;
d["b"] = 2;
var a = new KeyValuePair<string, object?>[2];

d.CopyTo(a, 0);

Assert.Equal("a", a[0].Key);
Assert.Equal(1, a[0].Value);
Assert.Equal("b", a[1].Key);
Assert.Equal(2, a[1].Value);
}

[Fact]
public void CopyTo_StartGreaterThanZero_Copies()
{
[Fact]
public void Dynamic_SetAndGet_Works()
{
dynamic obj = new FastDynamicObject();
obj.Id = 1;
obj.Name = "one";

var id = obj.Id;
var name = obj.Name;

Assert.Equal(1, id);
Assert.Equal("one", name);
}

[Fact]
public void CopyTo_NegativeIndex_Throws()
{
IDictionary<string, object> d = new FastDynamicObject();
var a = new KeyValuePair<string, object>[1];

Assert.Throws<ArgumentOutOfRangeException>(() => d.CopyTo(a, -1));
}

[Fact]
public void CopyTo_IndexLargerThanArrayLength_Throws()
{
IDictionary<string, object> d = new FastDynamicObject();
var a = new KeyValuePair<string, object>[1];

Assert.Throws<ArgumentOutOfRangeException>(() => d.CopyTo(a, a.Length));
}

[Fact]
public void CopyTo_SourceIsLargerThanDestination_Throws()
{
IDictionary<string, object> d = new FastDynamicObject();
d["a"] = 1;
d["b"] = 2;
var a = new KeyValuePair<string, object>[1];

Assert.Throws<ArgumentException>(() => d.CopyTo(a, 0));
}

[Fact]
public void CopyTo_IndexGreaterThanZeroAndSourceIsLargerThanDestination_Throws()
{
IDictionary<string, object> d = new FastDynamicObject();
d["a"] = 1;
d["b"] = 2;
var a = new KeyValuePair<string, object>[2];

Assert.Throws<ArgumentException>(() => d.CopyTo(a, 1));
}

[Fact]
public void CopyTo_StartAtZero_Copies()
{
IDictionary<string, object> d = new FastDynamicObject();
d["a"] = 1;
d["b"] = 2;
var a = new KeyValuePair<string, object>[2];

d.CopyTo(a, 0);

Assert.Equal("a", a[0].Key);
Assert.Equal(1, a[0].Value);
Assert.Equal("b", a[1].Key);
Assert.Equal(2, a[1].Value);
}

[Fact]
public void CopyTo_StartGreaterThanZero_Copies()
{
IDictionary<string, object> d = new FastDynamicObject();
d["a"] = 1;
d["b"] = 2;
var a = new KeyValuePair<string, object>[4];

d.CopyTo(a, 1);

Assert.Null(a[0].Key);
Assert.Null(a[0].Value);
Assert.Equal("a", a[1].Key);
Assert.Equal(1, a[1].Value);
Assert.Equal("b", a[2].Key);
Assert.Equal(2, a[2].Value);
Assert.Null(a[3].Key);
Assert.Null(a[3].Value);
}
IDictionary<string, object?> d = new FastDynamicObject();
d["a"] = 1;
d["b"] = 2;
var a = new KeyValuePair<string, object?>[4];

d.CopyTo(a, 1);

Assert.Null(a[0].Key);
Assert.Null(a[0].Value);
Assert.Equal("a", a[1].Key);
Assert.Equal(1, a[1].Value);
Assert.Equal("b", a[2].Key);
Assert.Equal(2, a[2].Value);
Assert.Null(a[3].Key);
Assert.Null(a[3].Value);
}
}

0 comments on commit f9e5fa4

Please sign in to comment.