Общий метод, который принимает интерфейс, который реализует другой интерфейс, не вызывая правильный метод - PullRequest
1 голос
/ 21 марта 2011

Я создаю самостоятельную поисковую сборку с интерфейсом ISearchable, который можно реализовать. Он также предоставляет возможность поиска по номеру телефона, поэтому у него есть IPhoneNumberSearchable, который реализует ISearchable. Затем я иду по сборке в поисках чего-либо, что реализует IPhoneNumberSearchable, и вызываю Поиск. Если класс реализует IPhoneNumberSearchable и ISearchable, он будет вызывать только метод ISearchable. Любые идеи о том, как сделать эту работу? Код следует

public class SearchManager
{



    private ISearchItem[] Search<T>(string searchValue) where T: class,ISearchable
    {

        Assembly current = System.Reflection.Assembly.GetExecutingAssembly();

        IEnumerable<T> instances = from t in Assembly.GetExecutingAssembly().GetTypes()
                        where t.GetInterfaces().Contains(typeof(T))
                                 && t.GetConstructor(Type.EmptyTypes) != null
                        select Activator.CreateInstance(t) as T;

        var list = new List<ISearchItem>();
        foreach (T item in instances)
        {
            try
            {
                T i = item as T;
                list.AddRange(item.Search(searchValue));
            }
            catch (System.Exception) { }
        }


        return list.ToArray();

    }


    /// <summary>
    /// Searches the specified search value.
    /// </summary>
    /// <param name="searchValue">The search value.</param>
    /// <returns></returns>
    public ISearchItem[] Search(string searchValue)
    {
        return Search<ISearchable>(searchValue);
    }


    /// <summary>
    /// Searches for phone number.
    /// </summary>
    /// <param name="phoneNumber">The phone number.</param>
    /// <returns></returns>
    public ISearchItem[] SearchForPhoneNumber(string phoneNumber)
    {
        return Search<IPhoneSearchable>(phoneNumber);
    }

}


/// <summary>
/// 
/// </summary>
public interface ISearchable
{

    ISearchItem[] Search(string searchValue);
}

/// <summary>
/// 
/// </summary>
public interface ISearchable
{

    ISearchItem[] Search(string searchValue);
}



public class CustomerManager : Search.IPhoneSearchable,Search.ISearchable
{


    /// <summary>
    /// Searches the specified phone number.
    /// </summary>
    /// <param name="phoneNumber">The phone number.</param>
    /// <returns></returns>
    Search.ISearchItem[] Search.IPhoneSearchable.Search(string phoneNumber)
    {
        //Search based upon phone number            
    }

    /// <summary>
    /// Searches the specified search value.
    /// </summary>
    /// <param name="searchValue">The search value.</param>
    /// <returns></returns>
    Search.ISearchItem[] Search.ISearchable.Search(string searchValue)
    {
        //Search on anything code
    }
}

Ответы [ 3 ]

1 голос
/ 21 марта 2011

Этот код не работает так, как вы ожидаете, потому что он работает точно в соответствии со спецификацией языка.

В основном в вашем классе CustomManager Вы реализуете ISearchable неявно и явно.Когда вы хотите попытаться вызвать этот метод в обычном коде (без использования отражения), это выглядит так:

CustomManager k = new CustomManager();
IPhoneSearchable x = k;
x.Search("strings"); //calls IPhoneSearchable.Search()
ISearchable y = k;
y.Search("string"); //calls ISearchable.Search()
k.Search("string); //calls ISearchable.Search()!!!

причина этого именно в том, чтобы не путать кодер.Вы реализуете интерфейс в классе, поэтому каждый вызов этого метода должен вызывать эту реализацию.Если по какой-то другой причине Вы реализуете другой интерфейс явно, Вам необходимо указать, что Вы хотите, чтобы этот точный вызов происходил.

Я не хочу вмешиваться в Ваш дизайн, но для меня это выглядит немного странно.Один метод ищет что-то, а другой ищет определенные вещи, но оба имеют одинаковую сигнатуру, предоставляя слегка отличающиеся функциональные возможности.Возможно, следует изменить, чтобы IPhoneSearchable предоставлял метод с именем

SearchPhones(string[] filters)

, который фактически вызывает метод Search, таким образом скрывая детали реализации от пользователя.Предоставляя Inteface IPhoneSearchable, вы ничего не делаете в спецификации контракта, кроме предоставления интерфейса маркера.

Итак, чтобы исправить свой код Вам, вероятно, следует создать коллекцию реальных MethodCalls (следовательно, в CustomManager есть два вызова метода, а именно Search fromISearchable и Search from IPhoneSearchable)

, итерация этой коллекции и вызов каждого метода.

Относительно Люка

0 голосов
/ 24 марта 2011

Моей главной проблемой было то, что универсальный тип был IPhoneSearchable, когда я вызывал поиск по универсальному типу (IPhoneSearchable), он все еще называл метод ISearchable.

Единственный способ заставить его работать -через отражение, используя следующий код для поиска звонков.

        var list = new List<ISearchItem>();
        foreach (T item in instances)
        {


            try
            {

                var type = typeof(T);

                var inter = item.GetType().GetInterface(type.Name);

                var results = (ISearchItem[])inter.InvokeMember("Search", BindingFlags.Instance | BindingFlags.InvokeMethod | BindingFlags.Public, null, item, new object[] { searchValue });
                if(results != null)
                    list.AddRange(results);
            }
            catch (System.Exception) { }
        }
0 голосов
/ 21 марта 2011

По причинам, объясненным удачей, ваш код не будет работать.Но вместо создания

коллекция фактических MethodCalls (следовательно, CustomManager имеет два вызова метода, а именно: Поиск из ISearchable и Поиск из IPhoneSearchable), а затем повторяет эту коллекцию и вызывает каждый метод

Вы могли бы сделать что-то вроде этого (я немного изменил ваш код):

public interface ISearch
{
    IEnumerable<string> Search(string filter);
}

public interface IPhoneSearch : ISearch
{
    new IEnumerable<string> Search(string filter);
}

public class Searchable : IPhoneSearch
{
    public IEnumerable<string> Search(string filter)
    {
        yield return "Phone!";
    }

    IEnumerable<string> ISearch.Search(string filter)
    {
        yield return "Normal!";
    }
}

А потом в вашем поисковике:

static IEnumerable<string> Search<T>(string searchValue) where T : class, ISearch
{
    var current = Assembly.GetExecutingAssembly();
    var instances = from t in Assembly.GetExecutingAssembly().GetTypes()
                    where !t.IsInterface
                    where typeof(T).IsAssignableFrom(t)
                    select (dynamic)Activator.CreateInstance(t);

    foreach (var item in instances)
    {
        foreach (var occurrence in item.Search(searchValue))
        {
            yield return occurrence;
        }
    }
}

В любом случае я бы предложил изменить этореализация, как сказал счастливчик.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...