简体   繁体   中英

Getting a Property list with similar name for looping c#

I have a class with several properties

public class Hotel
{
   public string ID {get; set;}
   public string Name {get; set;}
   public string Location {get; set;}
   public Image Image1 {get; set;}
   public Image Image2 {get; set;}
   public Image Image3 {get; set;}
   public Image Image4 {get; set;}
}

I want to get a list of all the properties starting with property name "Image" ex

public List<Image> GetImageList(Hotel hotel)
{
  List<Image> imageList = new List<Image>();
  foreach(var item in <ImageList>)
  {
    rest of the code .....
  }

  return imageList ;
}

Technically, as a quick and dirty solution, you can try reflection :

   using System.Reflection;

   ...

   // Properties; some conditions like property.CanRead && property.CanWrite 
   // can well appear redundant, but since the class is not yours,
   // better be a bit paranoic esp. having run into a bad design 
   var properties = hotel
     .GetType()
     .GetProperties(BindingFlags.Public | BindingFlags.Instance)
     .Where(property => property.CanRead && property.CanWrite) // Not necessary 
     .Where(property => property.Name.StartsWith("Image"))
     .Where(property => property.PropertyType == typeof(Image)); // Not necessary

   // Properties' values, i.e. images
   List<Image> list = properties
     .Select(property => property.GetValue(hotel) as Image)
     .Where(image => image != null) // if you want to filter out nulls
     .ToList(); 

However, it's the high time for you to redesign the routine:

  1. Normalize the db table (push images into a separate table)
  2. Redesign Image1..Image4 into IList<Image> or alike collection .

First of all you can get all the PropertyInfos using Reflection and reuse them when calling GetImageList with different Hotel instances:

private static List<PropertyInfo> hotelImages = GetHotelImageProperties();

private static List<PropertyInfo> GetHotelImageProperties()
{
    return typeof(Hotel)
        .GetProperties(BindingFlags.Instance | BindingFlags.Public)
        .Where(x => x.PropertyType == typeof(Image))
        .Where(x => x.CanRead)
        .Where(x => x.Name.StartsWith("Image"))
        .ToList();
}

Then, each time GetImageList is called, just get the values of the properties:

public static List<Image> GetImageList(Hotel hotel)
{
    return hotelImages
        .Select(x => x.GetValue(hotel))
        .Cast<Image>()
        .ToList();
}

With that said, it's important to notice that reflection is slow and should be avoided when it is not really required, more than that, the properties are probably known at compile time or can be represented as some IEnumerable<Image> to allow a better design (more flexible, fast and type-safe).

If it is generated from a database or some other scheme, you should consider representing it or generating it as some collection type.

Getting properties that way needs reflection, and since reflection isn't that great for performance. I got a much better solution for you:

public class Hotel
{
    public string ID {get; set;}
    public string Name {get; set;}
    public string Location {get; set;}
    public List<Image> Images {get; set;}
}

This way you can just use:

Hotel hotel = new Hotel();
// Set properties
List<Image> imagesFromHotel = hotel.Images;

Wouldn't that be MUCH easier? You can also easier add more images. You wouldn't need to add more properties for more images.

You can use reflection to loop through the Hotel properties, and take the values from the Image types:

public List<Image> GetImageList(Hotel hotel)
    {
        List<Image> imageList = new List<Image>();
        var type = hotel.GetType();
        var propertyInfos = type.GetProperties();
        foreach (var item in propertyInfos)
        {
            if (item.Name.StartsWith("Image") && item.PropertyType==typeof(Image))
            {
                imageList.Add((Image)item.GetValue(hotel));
            }
        }

        return imageList;
    }

This will give you an enumerable sequence of Image which returns the Image for each public property with a name that begins with "Image":

var images =
    from   property in hotel.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance)
    where  property.Name.StartsWith("Image") && property.PropertyType == typeof(Image)
    select (Image) property.GetValue(hotel);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM