2 Stimmen

Casting auf abstrakte Klasse oder Schnittstelle bei Verwendung von Generika

Ich habe diese Methode Verify_X, die während Databind für eine Listbox ausgewählten Wert aufgerufen wird. Das Problem ist die stark typisierte Datenquelle. Ich möchte die abstrakte Klasse BaseDataSource oder eine Schnittstelle verwenden, um die unterstützten Methoden aufzurufen: Parameter[] und Select(), anstatt die spezifischste Implementierung zu verwenden, wie unten zu sehen.

So kann eine Methode für alle verschiedenen Arten von Datenquellen verwendet werden, anstatt für jede eine eigene Methode zu haben. Sie erben alle auf die gleiche Weise.

Hier ist die Kette der Vererbung / Implementierung

public class DseDataSource : ProviderDataSource<SCCS.BLL.Dse, DseKey>

public abstract class ProviderDataSource<Entity, EntityKey> : BaseDataSource<Entity, EntityKey>, ILinkedDataSource, IListDataSource
    where Entity : SCCS.BLL.IEntityId<EntityKey>, new()
    where EntityKey : SCCS.BLL.IEntityKey, new()

public abstract class BaseDataSource<Entity, EntityKey> : DataSourceControl, IListDataSource, IDataSourceEvents
    where Entity : new()
    where EntityKey : new()

Die BaseDataSource hat die Methoden und Eigenschaften, die ich brauche. DseDataSource ist auf folgende Weise implementiert:

public class DseDataSource : ProviderDataSource<SCCS.BLL.Dse, DseKey>

Ich weiß, dass es möglich ist, die Klasse DseDataSource zu bearbeiten, eine Schnittstelle hinzuzufügen, um auf Parameter[] und Select() zuzugreifen, und dann gegen diese zu programmieren, was erlaubt, was ich möchte, aber dies erfordert die Bearbeitung der NetTiers-Bibliotheken, und ich bin neugierig zu sehen, ob dies getan werden kann, da es so schwierig schien.

    public static string Verify_DSE(string valueToBind, DseDataSource dataSource)
    {
        if (ListContainsValue(dataSource.GetEntityList(), valueToBind)) return valueToBind;
        CustomParameter p = dataSource.Parameters["WhereClause"] as CustomParameter;
        if (p != null)
        {
            p.Value = "IsActive=true OR Id=" + valueToBind;
            dataSource.Select();
            return valueToBind;
        }
        return string.Empty;
    }

    private static bool ListContainsValue(IEnumerable list, string value)
    {
        if (value.Length == 0) return true;

        foreach (object o in list)
        {
            IEntity entity = o as IEntity;
            if (entity != null)
            {
                if (entity.Id.ToString() == value)
                    return true;
            }
        }
        return false;
    }

Das Endergebnis wäre ein Code wie der folgende:

public static string Verify(string valueToBind, object dataSource)
{
//what is the correct way to convert from object
BaseDataSource baseInstance = dataSource as BaseDataSource;

if baseInstance != null)
{
    if (ListContainsValue(baseInstance.GetEntityList(), valueToBind)) return valueToBind;
    CustomParameter p = baseInstance.Parameters["WhereClause"] as CustomParameter;
    if (p != null)
    {
        p.Value = "IsActive=true OR Id=" + valueToBind;
        baseInstance.Select();
        return valueToBind;
    }
}

return string.Empty;
}

2voto

John Fisher Punkte 21825

Wenn Sie nicht in der Lage sind, die Klassendefinition zu ändern oder eine Art von Erweiterungsmethoden zu verwenden, können Sie Reflection verwenden. Hier ist ein Beispiel, das ich anhand von Annahmen über Ihren Code ausgearbeitet habe:

    public static string Verify(string valueToBind, object dataSource)
    {
        ////what is the correct way to convert from object
        //BaseDataSource baseInstance = dataSource as BaseDataSource;
        Type type = dataSource.GetType();
        MethodInfo select = type.GetMethod("Select");
        PropertyInfo parameters = type.GetProperty("Parameters");
        PropertyInfo parameterGetter = null;
        object parametersInstance = null;
        if (parameters != null)
        {
            parametersInstance = parameters.GetValue(dataSource, null);
            type = parametersInstance.GetType();
            parameterGetter = type.GetProperty("Item");
        }

        //if baseInstance != null)
        if (select != null && parameters != null && parameterGetter != null)
        {
                if (ListContainsValue(baseInstance.GetEntityList(), valueToBind)) return valueToBind;
                CustomParameter p = parameterGetter.GetValue(parametersInstance, new object[] {"WhereClause" }) as CustomParameter;

                if (p != null)
                {
                        p.Value = "IsActive=true OR Id=" + valueToBind;
                        select.Invoke(dataSource, null);
                        return valueToBind;
                }
        }

        return string.Empty;
    }

1voto

CRice Punkte 11793

Danke John, du hast mich da wirklich auf den richtigen Weg gebracht. Ich endete mit dem folgenden Code:

    public string Verify(string valueToBind, object dataSource)
    {
        IListDataSource listDataSource = dataSource as IListDataSource;
        if (listDataSource != null)
        {
            if (ListContainsValue(listDataSource.GetEntityList(), valueToBind)) return valueToBind;
        }

        Type type = dataSource.GetType();
        MethodInfo select = type.GetMethod("Select", new Type[0]);
        PropertyInfo parameterCollectionInfo = type.GetProperty("Parameters");
        ParameterCollection pc = parameterCollectionInfo.GetValue(dataSource, null) as ParameterCollection;

        if (pc != null)
        {
            CustomParameter p = pc["WhereClause"] as CustomParameter;
            if (p != null)
            {
                p.Value = "IsActive=true OR Id=" + valueToBind;
                select.Invoke(dataSource, null);
                return valueToBind;
            }
        }

        return string.Empty;
    }

0voto

Marc Gravell Punkte 970173

Da der Code "so wie er ist" nicht brauchbar ist, ist es sehr schwer, das eigentliche Problem zu erörtern... können Sie das Beispiel auf etwas vereinfachen, das nicht von externen Bibliotheken/Klassen abhängt?

Kann ich bestätigen: ist DseDataSource aus NetTiers generiert? Erzeugt NetTiers in diesem Fall "partielle" Klassen? Wenn ja, können Sie die Schnittstellen-Implementierung in einer zweiten Klassendatei hinzufügen:

namespace YourNamespace {
    partial class DseDataSource : IYourInterface {
        // implement interface
    }
}

Damit wird die Funktionalität der DseDataSource Klasse, ohne dass Sie den generierten Code bearbeiten müssen.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X