开发者

C# abstract class return derived type enumerator

If I have an abstract class, is there any way I can return an enumerator of the derived class's type? Or would I have to use generics in the base class, or a generic method? Here's a really dumbed down example of what I'm trying to do -

public abstract class Person {
    public IEnumerable<MyType> Search() {
        DbDataReader reader = Database.Instance.ExecuteReader(sql);
        while(reader.Read()) {
            MyType row = new MyType();
            row.Load(reader);
            yeild return row;
        }
    }

    private Load(DbDataReader reader) {
        //load instance from reader row
    }

    //declare properties that can be searched, such as Location
}

public class Programmer : Person {
    //declare properties that can be searched, such as Language
}

Then somewhere else I'd like to be able to call

Programmer programmer = new Programmer();
programmer.Location = "My city";
programmer.Language = "C#";
foreach(Programmer programmer in programmer.Search())
{
    //display list of c# programmers in my city
}

I know I can do this with a generic method, like Search<T>(), but I'd like to be able to call the search function from a class that does not know exactly type the Person is (for example, a base class for an AJAX handler)

If this cannot be done, can anyone give me an example or re开发者_JAVA百科ason why not? Or would it just be too hard to implement into the compiler?


There's no reason you can't make your Search method generic:

public IEnumerable<T> Search<T>() where T : MyType, new() {
    DbDataReader reader = Database.Instance.ExecuteReader(sql);
    while(reader.Read()) {
        T row = new T();
        row.Load(reader);
        yield return row;
    }
}

and call programmer.Search<Programmer>().

Where does MyType come from btw? Should that be Person?


See http://blogs.msdn.com/b/ericlippert/archive/2011/02/03/curiouser-and-curiouser.aspx

Covariance on virtual method return types is a fairly frequently requested feature, and I'd use it if I had it. It's never made the bar because (1) the CLR does not support it; we'd have to either generate lots of helper code behind the scenes to make it work, or convince the CLR team to change (the C++/CLI team did the former) (2) in most cases you can generate the necessary helper functions yourself quite easily; just make a "new" method that has the right return type that delegates its implementation to the virtual method, and (3) Anders does not consider it to be a particularly important feature. - Eric

So, No, it isn't possible, and YES, it's because it's too hard.


If you search an approach with this "pretty" usage I propose an ExtensionMethod. With an Ext you don't have to define the effective type of the Entity twice. programmer.Search<Programmer>() => programmer.Search()

public static PersonExtensions
{
    public static IEnumerable<TType> Search<TType>(this TType row) : where TType : new(), Person 
    {
        DbDataReader reader = Database.Instance.ExecuteReader(sql);
        while(reader.Read()) {
            var row = new TType()
            row.Load(reader);
            yeild return row;
        }
    }
}


@ben dotnet: the fact of not having to pass the effective type explicitely is not due to the fact that it's an extension method. It is due to the type inference mecanism and would occur as well on any kind of method (extension or regular).

In such a method prototype

public static IEnumerable<TType> Search<TType>(this TType row) : where TType : new(), Person

TType is known by the compiler to be the same the 4 times it occurs in the line. Thus, when calling the method it's enough to know the type of programmer to know that it will be the same type between the <...>. That's why we can omit it. It works also if there is no parameter prefixed with the this keyword.


You could use a generic type in your base class and define your generic type in your derived class as the derived class.

public abstract class Person<T> where T : Person<T>
    {
        public IEnumerable<T> Search()
        {
            DbDataReader reader = Database.Instance.ExecuteReader(sql);
            while (reader.Read())
            {
                var row = new T();
                row.Load(reader);
                yield return row;
            }
        }

        protected virtual void Load(DbDataReader reader){}
    }

    public class Programmer : Person<Programmer>{}
0

上一篇:

下一篇:

精彩评论

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

最新问答

问答排行榜