Kann ich eine generische Methode erstellen, die einen Werttyp oder einen Referenztyp verwendet, aber immer einen nullfähigen Typ zurückgibt?

Das ist meine Methode. Beachten Sie, dass ich den entsprechenden nullwertfähigen Typ für den generischen Parameter zurückgebeR:

    public static Nullable<R> GetValue<T, R>(this T a, Expression<Func<T, R>> expression)
        where T : Attribute
        where R : struct
    {
        if (a == null)
            return null;

        PropertyInfo p = GetProperty(expression);
        if (p == null)
            return null;

        return (R)p.GetValue(a, null);
    }

Ich kann es in einem Aufruf verwenden, um den Wert eines Attributs wie folgt zu erhalten:

//I don't throw exceptions for invalid or missing calls 
//because I want to chain the calls together:
int maximumLength4 = instance.GetProperty(x => x.ToString())
                            .GetAttribute<StringLengthAttribute>()
                            .GetValue(x => x.MaximumLength)
                            .GetValueOrDefault(50);

Ich möchte die gleiche generische Methode mit Zeichenfolgen verwenden:

//I'd like to use the GetValue generic method with strings as well as integers 
string erroMessage = instance.GetProperty(x => x.ToString())
                            .GetAttribute<StringLengthAttribute>()
                            .GetValue(x => x.ErrorMessage);

aber es wird nicht kompiliert:

Der Typ 'R' muss ein nicht nullwertfähiger Wert sein, um ihn als Parameter 'T' im generischen Typ oder in der generischen Methode 'System.Nullable' zu verwenden.

Kann Typ 'string?' Nicht implizit konvertieren 'string'

Gibt es einen Trick, mit dem ich die gleiche Methodensignatur hier abrufen und dennoch generische Methoden verwenden können, um den Rückgabetyp auf einen Wert zu schließen, der null sein kann?

Dies ist ein Testcode, der zeigt, dass er für ganzzahlige Werte funktioniert:

//[StringLength(256)]
//public string Name { get; set; }
PropertyInfo info = ReflectionAPI.GetProperty<Organisation, String>(x => x.Name);//not null
StringLengthAttribute attr = info.GetAttribute<StringLengthAttribute>();//not null
int? maximumLength = attr.GetValue(x => x.MaximumLength);//256
int? minimumLength = attr.GetValue(x => x.MinimumLength);//0

PropertyInfo info2 = ReflectionAPI.GetProperty<Organisation, int>(x => x.ID);//not null
StringLengthAttribute attr2 = info2.GetAttribute<StringLengthAttribute>();//null because ID doesn't have the attribute
int? maximumLength2 = attr2.GetValue(x => x.MaximumLength);//null
int? minimumLength2 = attr2.GetValue(x => x.MinimumLength);//null

//I can use the GetProperty extension method on an instance
Organisation instance = (Organisation)null;
PropertyInfo info3 = instance.GetProperty(x => x.ToString());//null because its a method call not a property
StringLengthAttribute attr3 = info3.GetAttribute<StringLengthAttribute>();//null
int? maximumLength3 = attr3.GetValue(x => x.MaximumLength);//null
int? minimumLength3 = attr3.GetValue(x => x.MinimumLength);//null

Und das ist der Rest von mirReflectionAPI:

public static class ReflectionAPI
{

    public static Nullable<R> GetValue<T, R>(this T a, Expression<Func<T, R>> expression)
        where T : Attribute
    {
        if (a == null)
            return null;

        PropertyInfo p = GetProperty(expression);
        if (p == null)
            return null;

        return (R)p.GetValue(a, null);
    }

    public static T GetAttribute<T>(this PropertyInfo p) where T : Attribute
    {
        if (p == null)
            return null;

        return p.GetCustomAttributes(false).OfType<T>().LastOrDefault();
    }

    public static PropertyInfo GetProperty<T, R>(Expression<Func<T, R>> expression)
    {
        if (expression == null)
            return null;

        MemberExpression memberExpression = expression.Body as MemberExpression;
        if (memberExpression == null)
            return null;

        return memberExpression.Member as PropertyInfo;
    }
}

Antworten auf die Frage(1)

Ihre Antwort auf die Frage