Generically populate different classes members

2019-02-21 04:45发布

I am working on a web-service application with several (11) web-service calls.

For each web-service I need to populate the Soap Body from a string array like this:

if (aMessage[(int)DCSSCustomerUpdate_V3.Branch].ToString().Length != 0)
{
    wsSoapBody.Branch = aMessage[(int)DCSSCustomerUpdate_V3.Branch].ToString();
}

aMessage[int] is the string array, and [int] is defined by an enumerated constant - in this case it is defined like this:

private enum DCSSCustomerUpdate_V3
{
    MsgType = 0,
    MsgVersion = 1,
    WSName = 2,
    ReplyTo = 3,
    SourceSystem = 4,
    ...
}

The property names in the partial class are matched by the enumerated constant, so I guess I'd pass in the enumerated constant as well?

The partial class is defined in the wsdl like this:

public partial class DCSSCustomerUpdateType 
{
    private string instIdField;
    private string branchField;
    ...
}

Rather than doing this for each one separately (in each of 11 custom service classes), I wonder is there a way to pass in the partial class wsSoapBody (along with the string array) and loop through all the members of the class, assigning values from the string array?

EDIT:

I searched and found SO: 531384/how-to-loop-through-all-the-properties-of-a-class?

So I tried this:

    public static void DisplayAll(Object obj, string[] aMessage)
    {
        Type type = obj.GetType();
        PropertyInfo[] properties = type.GetProperties();

        foreach (PropertyInfo property in properties)
        {
            string value = aMessage[property.Name].ToString();
            System.Diagnostics.Debug.WriteLine("Name: " + property.Name + ", Value: " + property.GetValue(obj, null));
        }
     }

but string value = aMessage[property.Name].ToString(); won't compile - as it is looking for an int returned from an enumerated constant...

so where do I go from there?

3条回答
\"骚年 ilove
2楼-- · 2019-02-21 05:19

so thanks to Fabio and Sloth, here is the final code we built:

public static void DisplayAll<TEnum>(Object obj, string[] aMessage) where TEnum : struct,  IComparable, IFormattable, IConvertible
/* 
 * see https://stackoverflow.com/questions/28168982/generically-populate-different-classes-members
 * 
 */
{
try
{
    // get the type of wsSoapBody
    Type type = obj.GetType();

    PropertyInfo[] properties = type.GetProperties();

    foreach (PropertyInfo property in properties)
    {
    try
    {
        if (Enum.IsDefined(typeof(TEnum), property.Name))
        {

        TEnum t = (TEnum)Enum.Parse(typeof(TEnum), property.Name, true);

        System.Diagnostics.Debug.WriteLine("Name: " + property.Name + ", Value: " + property.GetValue(obj, null) + "Type: " + property.PropertyType);

        // property.GetValue(obj, null).ToString() != "" &&
        if ( t.ToInt32(Thread.CurrentThread.CurrentCulture) < aMessage.GetUpperBound(0) && aMessage[t.ToInt32(Thread.CurrentThread.CurrentCulture)].ToString() != "")
        {
            switch (property.PropertyType.ToString())
            {
            case "System.String":
                string value = aMessage[t.ToInt32(Thread.CurrentThread.CurrentCulture)].ToString();
                property.SetValue(obj, value, null);
                break;
            case "System.Int32":
                int iValue = Convert.ToInt32(aMessage[t.ToInt32(Thread.CurrentThread.CurrentCulture)].ToString());
                property.SetValue(obj, iValue, null);
                break;
            case "System.Int64":
                long lValue = Convert.ToInt64(aMessage[t.ToInt32(Thread.CurrentThread.CurrentCulture)].ToString());
                property.SetValue(obj, lValue, null);
                break;
            case "System.DateTime":
                DateTime dtValue = DateTime.ParseExact(aMessage[t.ToInt32(Thread.CurrentThread.CurrentCulture)].ToString(), "ddMMyyyy", System.Globalization.CultureInfo.InvariantCulture);
                property.SetValue(obj, dtValue, null);
                break;
            default:
                System.Diagnostics.Debugger.Break();
                break;
            }
        }
        else
        {
            logBuilder("Common.DisplayAll", "Info", "", property.Name + " is empty or outside range", "Index number: " + t.ToInt32(Thread.CurrentThread.CurrentCulture).ToString());

            System.Diagnostics.Debug.WriteLine(property.Name + " is empty or outside range", "Index number: " + t.ToInt32(Thread.CurrentThread.CurrentCulture).ToString());
        }
    }
    else
    {
        logBuilder("Common.DisplayAll", "Info", "", property.Name + " is not defined in Enum", "");
        System.Diagnostics.Debug.WriteLine(property.Name + " is not defined in Enum");
    }
    }
    catch (Exception ex)
    {
        logBuilder("Common.DisplayAll", "Error", "", ex.Message, "");
        emailer.exceptionEmail(ex);
        System.Diagnostics.Debugger.Break();
    }

    System.Diagnostics.Debug.WriteLine("Name: " + property.Name + ", Value: " + property.GetValue(obj, null));
    }
}
catch (Exception ex)
{
    logBuilder("Common.DisplayAll", "Error", "", ex.Message, "");
    emailer.exceptionEmail(ex);
    System.Diagnostics.Debugger.Break();
    //throw;
}
return;
}

and to call it, we use:

Common.DisplayAll<DCSSCustomerUpdate_V3>(wsSoapBody, aMessage);
查看更多
混吃等死
3楼-- · 2019-02-21 05:29

Try so

DCSSCustomerUpdate_V3 t = (DCSSCustomerUpdate_V3)Enum.Parse(typeof(DCSSCustomerUpdate_V3), property.Name);
 string value = aMessage[(int)t].ToString();

you can use also the method Enum.TryParse

For generic Enum type more or less so

 public static void DisplayAll<TEnum>(Object obj, string[] aMessage) where TEnum : struct,  IComparable, IFormattable, IConvertible
        {
            if (!typeof(TEnum).IsEnum)
            {
                throw new ArgumentException("T must be an enumerated type");
            }

            Type type = obj.GetType();
            PropertyInfo[] properties = type.GetProperties();

            foreach (PropertyInfo property in properties)
            {
                TEnum t = (TEnum)Enum.Parse(typeof(TEnum), property.Name);
                string value = aMessage[t.ToInt32(Thread.CurrentThread.CurrentCulture)].ToString();
                System.Diagnostics.Debug.WriteLine("Name: " + property.Name + ", Value: " + property.GetValue(obj, null));
            }
        }

see this post Create Generic method constraining T to an Enum

查看更多
叛逆
4楼-- · 2019-02-21 05:35

I don't know if I understood your question:

if (aMessage[(int)DCSSCustomerUpdate_V3.Branch].ToString().Length != 0)
{
    wsSoapBody.Branch = aMessage[(int)DCSSCustomerUpdate_V3.Branch].ToString();
}

So you have this enum DCSSCustomerUpdate_V3 which members match the property names of the wsSoapBody class, and you don't want to repeat code like the one above, but use a loop, correct?

You could simply loop over all elements of DCSSCustomerUpdate_V3 and set the value of the properties like:

// type of the enum; pass in as parameter
var enumType = typeof(DCSSCustomerUpdate_V3)

// get the type of wsSoapBody
var t = wsSoapBody.GetType();

// loop over all elements of DCSSCustomerUpdate_V3
foreach(var value in Enum.GetValues(enumType))
{
    if (aMessage[(int)value].ToString().Length != 0)
    {
        // set the value using SetValue
        t.GetProperty(value.ToString()).SetValue(wsSoapBody, aMessage[(int)value].ToString());
    }
}
查看更多
登录 后发表回答