.NET Reflection
GetProperties method
BindingFlags
DeclaredOnly
C#

Using GetProperties with BindingFlags.DeclaredOnly in .NET Reflection

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

BindingFlags.DeclaredOnly is useful when reflection should return only the properties introduced on the current type, not the ones inherited from base classes. That sounds simple, but it is easy to forget that DeclaredOnly does not stand alone. You still need to combine it with flags such as Instance, Static, Public, or NonPublic to describe which declared properties you actually want.

What DeclaredOnly Actually Means

When you call GetProperties without DeclaredOnly, reflection can include inherited properties that are visible under the binding rules you specified. Adding DeclaredOnly narrows the result set to members defined directly on the target type.

This is especially useful when:

  • comparing a derived type to its base type
  • generating metadata only for properties introduced by the current class
  • implementing custom mapping or serialization rules for one layer of the hierarchy

A Simple Example

csharp
1using System;
2using System.Linq;
3using System.Reflection;
4
5public class Person
6{
7    public string Name { get; set; } = "";
8}
9
10public class Employee : Person
11{
12    public int EmployeeId { get; set; }
13}
14
15public static class Program
16{
17    public static void Main()
18    {
19        var all = typeof(Employee).GetProperties(BindingFlags.Instance | BindingFlags.Public);
20        var declared = typeof(Employee).GetProperties(
21            BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly);
22
23        Console.WriteLine("All:");
24        foreach (var property in all)
25        {
26            Console.WriteLine(property.Name);
27        }
28
29        Console.WriteLine("Declared only:");
30        foreach (var property in declared)
31        {
32            Console.WriteLine(property.Name);
33        }
34    }
35}

Expected behavior:

  • 'all contains Name and EmployeeId'
  • 'declared contains only EmployeeId'

That is the core behavior of DeclaredOnly.

Why the Other Binding Flags Still Matter

DeclaredOnly does not imply instance-only or public-only behavior. It only filters by where the member was declared. You still need to describe the visibility and member kind you want.

Examples:

  • 'BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly'
  • 'BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.DeclaredOnly'

If you omit Instance and Static, or omit Public and NonPublic, you can easily get an empty result and think reflection is broken.

Declared Properties Versus Hidden Properties

If a derived type hides a base property using new, the new property is declared on the derived type and therefore can appear when using DeclaredOnly.

csharp
1using System;
2using System.Reflection;
3
4public class BaseItem
5{
6    public string Code { get; set; } = "base";
7}
8
9public class DerivedItem : BaseItem
10{
11    public new string Code { get; set; } = "derived";
12}
13
14public static class Program
15{
16    public static void Main()
17    {
18        var props = typeof(DerivedItem).GetProperties(
19            BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly);
20
21        foreach (var property in props)
22        {
23            Console.WriteLine(property.Name);
24        }
25    }
26}

This returns the derived declaration, not the inherited base property.

A Common Real-World Use Case

Suppose you are building a mapper that should copy only the fields introduced by a derived DTO layer. DeclaredOnly prevents inherited infrastructure properties from polluting the mapping list.

That keeps reflection-based code more intentional, especially in class hierarchies with framework-level base classes.

Performance and Reuse

Reflection is slower than direct property access, so if you use GetProperties repeatedly for the same types, cache the results.

csharp
1using System;
2using System.Collections.Concurrent;
3using System.Reflection;
4
5public static class PropertyCache
6{
7    private static readonly ConcurrentDictionary<Type, PropertyInfo[]> Cache = new();
8
9    public static PropertyInfo[] GetDeclaredPublicInstanceProperties(Type type)
10    {
11        return Cache.GetOrAdd(
12            type,
13            t => t.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly)
14        );
15    }
16}

That pattern matters in serializers, mappers, and validation frameworks.

Common Pitfalls

  • Assuming DeclaredOnly by itself is enough without Instance, Static, Public, or NonPublic.
  • Expecting inherited properties to appear even though DeclaredOnly explicitly filters them out.
  • Forgetting that a hidden member declared with new still counts as declared on the derived type.
  • Using reflection repeatedly in hot paths without caching.
  • Confusing properties with fields, which require GetFields and the corresponding binding flags.

Summary

  • 'BindingFlags.DeclaredOnly returns only properties introduced on the current type.'
  • It must be combined with other binding flags that define visibility and member kind.
  • It is useful when inherited properties would create noise or incorrect metadata.
  • Hidden properties declared in the derived class still count as declared members.
  • Cache reflection results if the same lookup happens frequently.

Course illustration
Course illustration

All Rights Reserved.