Diego Alves
Diego Alves

Reputation: 2687

C# get all properties of a certain type of an object in C#

I have an object that has 10 IFormFiles. I want to put them into a list of IFormFile so that it can be manipulated easily.

public class Car{

 public IFormFile Imagem1 {get; set;}
 public IFormFile Imagem2 {get; set;}
......

}



 List<IFormFile> imagens = new List<IFormFile>();

foreach(IFormFile imagem in carro.)
  {
          imagens.add(...);
  }

Is there a way to pass the IFormFile to a lista, or will I have to manipulate use them in the Car object.

I forgot to say I was adding them using

images.add(car.Image1);

But my code is getting messy. Because I have to check for nulls and many other things. If I could get the IFormFile in a loop my life will be much easier.

Upvotes: 10

Views: 10452

Answers (3)

Ramil Aliyev 007
Ramil Aliyev 007

Reputation: 5470

You may be use reflection. GetProperties method return all of properties specific type.

For example your code must be as below :

using Microsoft.AspNetCore.Http;
using System;
using System.Collections.Generic;
using System.Linq;

namespace ConsoleApp1
{
    public class Car
    {

        public IFormFile Imagem1 { get; set; }
        public IFormFile Imagem2 { get; set; }
        public IFormFile Imagem3 { get; set; }
        //and etc
    }

    public class Example
    {
        public static void Main()
        {
            List<IFormFile> imagens = new List<IFormFile>();

            var car = new Car();

            var carType = car.GetType();
            var ifromFileProperties = carType.GetProperties().Where(x => x.PropertyType == typeof(IFormFile)).ToArray();

            foreach (var property in ifromFileProperties)
            {
                var image = (IFormFile)property.GetValue(car, null);
                imagens.Add(image);
            }
        }
    }
}

At last there are all the items which type of property is IFormFile in the list

Upvotes: 0

AndreyCh
AndreyCh

Reputation: 1413

The code below demonstrates how to get all the properties of type IFormFile from the object car.

As it was mentioned in the comments, Reflection API is quite slow - consider caching PropertyInfo objects, or what is better - using Expressions to compile a delegate, that would iterate over the object properties and put their values into a target collection.

void Main()
{
    var car = new Car();

    var imagens = typeof(Car).GetProperties()
        .Where(x => x.PropertyType == typeof(IFormFile))
        .Select(x => (IFormFile)x.GetValue(car))
        .Where(x => x != null)
        .ToList();
}

PropertyInfo caching

Below is a sample of how the code above can be transformed to use cached PropertyInfo objects:

void Main()
{
    var car = new Car();
    var imagens = PropertyGetter<Car, IFormFile>.GetValues(car);
}

public static class PropertyGetter<TObject, TPropertyType>
{
    private static readonly PropertyInfo[] _properties;

    static PropertyGetter()
    {
        _properties = typeof(TObject).GetProperties()
            // "IsAssignableFrom" is used to support derived types too
            .Where(x => typeof(TPropertyType).IsAssignableFrom(x.PropertyType))
            // Check that the property is accessible
            .Where(x => x.GetMethod != null && x.GetMethod.IsPublic && !x.GetMethod.IsStatic)
            .ToArray();
    }

    public static TPropertyType[] GetValues(TObject obj)
    {
        var values = _properties
            .Select(x => (TPropertyType) x.GetValue(obj))
            .ToArray();

        return values;
    }
}

Expression-based

Another sample showing how it's possible to implement the logic of selecting property values of specific type based on Expressions.

public static class PropertyGetterEx<TObject, TPropertyType>
{
    private static readonly Func<TObject, TPropertyType[]> _getterFunc;

    static PropertyGetterEx()
    {
        // The code below constructs the following delegate:
        //
        // o => object.ReferenceEquals(o, null)
        //      ? null
        //      : new TPropertyType[] { o.Prop1, o.Prop2, ... };
        //

        // An expression for the parameter `o`
        var objParam = Expression.Parameter(typeof(TObject), "o");

        // Create expressions for `o.Prop1` ... `o.PropN`
        var propertyAccessExprs = GetPropertyInfos()
            .Select(x => Expression.MakeMemberAccess(objParam, x));

        // Create an expression for `new TPropertyType[] { o.Prop1, o.Prop2, ... }`
        var arrayInitExpr = Expression.NewArrayInit(
            typeof(TPropertyType),
            propertyAccessExprs);

        // Create an expression for `object.ReferenceEquals(o, null)`
        var refEqualsInfo = typeof(object).GetMethod(nameof(object.ReferenceEquals));
        var refEqualsExpr = Expression.Call(
            refEqualsInfo,
            objParam,
            Expression.Constant(null, typeof(TPropertyType)));

        // The condition expression
        var conditionExpr = Expression.Condition(
            refEqualsExpr,
            Expression.Constant(null, typeof(TPropertyType[])),
            arrayInitExpr);

        _getterFunc = Expression
            .Lambda<Func<TObject, TPropertyType[]>>(
                conditionExpr,
                objParam)
            .Compile();
    }

    private static PropertyInfo[] GetPropertyInfos()
    {
        var properties = typeof(TObject).GetProperties()
            // "IsAssignableFrom" is used to support derived types too
            .Where(x => typeof(TPropertyType).IsAssignableFrom(x.PropertyType))
            // Check that the property is accessible
            .Where(x => x.GetMethod != null && x.GetMethod.IsPublic && !x.GetMethod.IsStatic)
            .ToArray();

        return properties;
    }

    public static TPropertyType[] GetValues(TObject obj)
    {
        return _getterFunc(obj);
    }
}

Benchmark results

Below are benchmark results for the 3 approaches provided above (no cache, with PropertyInfo cache, Expression-based). As expected, the Expression-based solution performs much better than the others:

|               Method |      Mean |    Error |   StdDev | Rank |
|--------------------- |----------:|---------:|---------:|-----:|
|              NoCache | 789.99 ns | 4.669 ns | 4.139 ns |    3 |
|    PropertyInfoCache | 417.32 ns | 3.271 ns | 3.059 ns |    2 |
| ExpressionBasedCache |  27.55 ns | 0.091 ns | 0.085 ns |    1 |

Upvotes: 12

GregorMohorko
GregorMohorko

Reputation: 2877

Here is a method that returns all properties of the specified type from the provided object:

public static List<TProperty> GetAllPropertyValuesOfType<TProperty>(this object obj)
{
    return obj.GetType()
        .GetProperties()
        .Where(prop => prop.PropertyType == typeof(TProperty))
        .Select(pi => (TProperty)pi.GetValue(obj))
        .ToList();
}

You can use it like this:

Car myCar;
List<IFormFile> imagesOfMyCar = myCar.GetAllPropertyValuesOfType<IFormFile>();

Upvotes: 7

Related Questions