简体   繁体   中英

C# How to access properties with the same name on different objects, without reflection?

Let's say I have n objects:

class RedSubscriber {

String msisdn {get; set;}
String imsi {get; set;}
...other unique properties specific for RedSubscriber

}


class BlueSubscriber {

String msisdn {get; set;}
String imei {get; set;}
...other unique properties specific for BlueSubscriber

}

  class YellowSubscriber {
    
    String msisdn {get; set;}
    String iccid {get; set;}
    ...other unique properties specific for YellowSubscriber
    
    }

And so on, for n objects.

Let's say I want to get the "String msisdn" value of any object, regardless of what color it is. I currently do it using Reflection:

public void getSubscriberMsisdn(Object subscriber){

 Type myType = subscriber.GetType();
  IList<PropertyInfo> props = new List<PropertyInfo>(myType.GetProperties());

            foreach (PropertyInfo propertyInfo in props)
            {
                if (propertyInfo == null)
                {
                    continue;
                }

                var propertyValue = propertyInfo.GetValue(subscriber);

                if (propertyValue == null)
                {
                    continue;
                }

                if(propertyInfo.Name.Equals("msisdn")){

                string msisdnValue = propertyValue.ToString();
                Debug.WriteLine("The msisdn is: " + msisdnValue);
                }
            }    

}

As you can see the code "works", but is absolutely wasteful to call it everytime I need to retrieve some values. Is there a way to access a property (that always maintains the same name and type) that is placed inside different objects, without using the aforementioned reflection method? Any tip will be appreciated.

Yes. Create an interface:

public interface ISubscriber
{
    public string msisdn {get; set;};
}

Change your classes to implement the interface, eg:

public class RedSubscriber : ISubscriber
{
    string msisdn {get; set;}
    string imsi {get; set;}
    ...
}

Create a getSubscriberMsisdn() method that takes an ISubscriber :

public void getSubscriberMsisdn(ISubscriber subscriber)
{
    Debug.WriteLine("The msisdn is: " + subscriber.msisdn);
}

Don't use reflection for polymorphism. Interfaces is a language feature that directly supports polymorphism and supports strong typing at compile time.

In situations like this there are two standard options:

  1. Interface
  2. Inheritance

Where to use which not always clear and very often both approaches work.

I think inheritance could work here because your models have a very strong "IS-A" relationship. It could look something like this:

abstract record Subscriber
{
    public required string Msisdn { get; init; }
}

record RedSubscriber : Subscriber
{
    public required string Imsi { get; init; }
}

record BlueSubscriber : Subscriber
{
    public required string Imei { get; init; }
}

And then

void F(Subscriber s)
{
    Console.WriteLine(s.Msisdn);
}

Notes:

  1. I capitalised the names of properties, it's the C# way.
  2. I took advantage of 'required'+'init' properties to ensure they are never null
  3. I used records instead of classes because it seems like a good fit: simple classes with value - rather than reference - equality.
  4. You don't need to use records - just replace 'record' with 'class' in the code above.

BTW. You could remove some of the boiler plate code by using positional records with more concise syntax:

abstract record Subscriber(string Msisdn);

sealed record RedSubscriber(
    string Msisdn, 
    string Imsi)
    : Subscriber(Msisdn: Msisdn);

sealed record BlueSubscriber(
    string Msisdn, 
    string Imei)
    : Subscriber(Msisdn: Msisdn);

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