Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for [DuckPropertyOrField] #6463

Merged
merged 1 commit into from
Dec 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 10 additions & 3 deletions tracer/src/Datadog.Trace/DuckTyping/DuckAttribute.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,21 @@ namespace Datadog.Trace.DuckTyping
internal enum DuckKind
{
/// <summary>
/// Property
/// The target member is a Property
/// </summary>
Property,

/// <summary>
/// Field
/// The target member is a Field
/// </summary>
Field
Field,

/// <summary>
/// The target member could be a Property or a Field.
/// Both members will be checked for, the first matching member will be used.
/// Property members are checked for first.
/// </summary>
PropertyOrField
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// <copyright file="DuckPropertyOrFieldAttribute.cs" company="Datadog">
// Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License.
// This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc.
// </copyright>

#nullable enable
namespace Datadog.Trace.DuckTyping;

/// <summary>
/// Duck attribute where the underlying member could be a field or a property
/// </summary>
internal class DuckPropertyOrFieldAttribute : DuckAttribute
{
/// <summary>
/// Initializes a new instance of the <see cref="DuckPropertyOrFieldAttribute"/> class.
/// </summary>
public DuckPropertyOrFieldAttribute()
{
Kind = DuckKind.PropertyOrField;
}
}
12 changes: 12 additions & 0 deletions tracer/src/Datadog.Trace/DuckTyping/DuckType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -661,9 +661,15 @@ private static void CreateProperties(TypeBuilder? proxyTypeBuilder, Type proxyDe
switch (duckAttribute.Kind)
{
case DuckKind.Property:
case DuckKind.PropertyOrField:
PropertyInfo? targetProperty = GetTargetPropertyOrIndex(targetType, duckAttribute.Name, duckAttribute.BindingFlags, proxyProperty);
if (targetProperty is null)
{
if (duckAttribute.Kind == DuckKind.PropertyOrField)
{
goto case DuckKind.Field;
}

if (proxyProperty.CanRead && proxyProperty.GetMethod is not null)
{
var getMethod = proxyProperty.GetMethod;
Expand Down Expand Up @@ -940,9 +946,15 @@ private static void CreatePropertiesFromStruct(TypeBuilder? proxyTypeBuilder, Ty
switch (duckAttribute.Kind)
{
case DuckKind.Property:
case DuckKind.PropertyOrField:
PropertyInfo? targetProperty = GetTargetProperty(targetType, duckAttribute.Name, duckAttribute.BindingFlags);
if (targetProperty is null)
{
if (duckAttribute.Kind == DuckKind.PropertyOrField)
{
goto case DuckKind.Field;
}

DuckTypePropertyOrFieldNotFoundException.Throw(proxyFieldInfo.Name, duckAttribute.Name, targetType);
continue;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
// <copyright file="PropertyOrFieldTests.cs" company="Datadog">
// Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License.
// This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc.
// </copyright>

using FluentAssertions;
using Xunit;

namespace Datadog.Trace.DuckTyping.Tests;

public class PropertyOrFieldTests
{
[Fact]
public void PropertyOrFieldTestUsesPropertyIfAvailable()
{
var target = new TargetWithProperty();
var proxy = target.DuckCast<Proxy>();

proxy.Value.Should().Be(target.Value);
}

[Fact]
public void PropertyOrFieldTestUsesFieldIfAvailable()
{
var target = new TargetWithField();
var proxy = target.DuckCast<Proxy>();

proxy.Value.Should().Be(target.Value);
}

[Fact]
public void PropertyOrFieldTestUsesNameFromDuckAttribute()
{
var target = new TargetWithFieldAndProperty();
var proxy = target.DuckCast<ProxyWithName>();

proxy.Value.Should().Be(target.GetValue());
}

[DuckCopy]
public struct Proxy
{
[DuckPropertyOrField]
public int Value;
}

[DuckCopy]
public struct ProxyWithName
{
[DuckPropertyOrField(Name = "value")]
public int Value;
}

public class TargetWithProperty
{
public int Value { get; } = 1;
}

public class TargetWithField
{
#pragma warning disable SA1401 // should be private
public int Value = 1;
#pragma warning restore SA1401
}

public class TargetWithFieldAndProperty
{
private int value = 3;

public int Value { get; } = 4;

public int GetValue() => value;
}
}
Loading