开发者

Generic Method That Takes an interface that implements another interface, not calling correct method

开发者 https://www.devze.com 2023-02-18 22:30 出处:网络
I am create a self searching assembly, that has an ISearchable interface that can be implemented. It also provides the ability to search on a phone number so it has an IPhoneNumberSearchable that impl

I am create a self searching assembly, that has an ISearchable interface that can be implemented. It also provides the ability to search on a phone number so it has an IPhoneNumberSearchable that implements ISearchable. I then go through the assembly looking for anything that implements the IPhoneNumberSearchable and call Search. If the class implements IPhoneNumberSearchable and ISearchable it will only call the ISearchable method. Any ideas on how to make this work? Code Follows

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[开发者_如何学JAVA] 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
    }
}


This code does not work as You expect, because it work precisely according to the Language specification.

Basically in You class CustomManager You implement ISearchable implicitly and explicitly. When YOu want to try to call this method in a regular code (not using the reflection) it looks like that

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()!!!

the reason for this precisely not to confuse the coder. You implement the interface in a class so each call to this method should call this implementation. If for some other reason You implement another interface explicitly You need to specify that You want this precise call to happen.

I do not want to intefere with Your design but for me this looks a bit weird. One method searches for something, and the other searches for specific stuff, but both have the same signature while providing slightly different functionality. Maybe It should be changed that IPhoneSearchable provides a method called

SearchPhones(string[] filters)

that actually calls a method Search thus hiding implementation details from the user. By providing an Inteface IPhoneSearchable you do nothing in the contract specification other that providing a marker interface.

So to fix Your code You should probably create a collection of actual MethodCalls (hence CustomManager has two method calls that is Search from ISearchable and Search from IPhoneSearchable)

and the iterate this collection and call each method.

regards Luke


For the reasons explained by luckyluke your code won't work. But, instead of creating

a collection of actual MethodCalls (hence CustomManager has two method calls that is Search from ISearchable and Search from IPhoneSearchable) and then iterate this collection and call each method

you could do something like this (I have slightly changed your code):

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!";
    }
}

And then in your searcher:

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;
        }
    }
}

Anyway I would suggest to change this implementation, as luckyluke said.


My main issue was even though the generic type was the IPhoneSearchable, when I called search upon the generic type(IPhoneSearchable) it still called the ISearchable method.

The only way I could get it to work was through reflection using the following code to call search.

        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

精彩评论

暂无评论...
验证码 换一张
取 消