简体   繁体   中英

define a generic property in non-generic base class

I dont think what I am trying to do is possible; is there a way to actually make this work?

There is a Base class from which a variety of different classes are derived. Derived classes can be generic or not; instances of the derived classes are added to a collection of type Base in WindowViewModel . The Base class has a collection of Options that are accessed by the WindowViewModel .

The issue is: the IOption interface declares a return type of Func<object, bool> MyFunc but the return type of MyFunc needs to be Func<T, bool> for the generic class method RunIt() and for the assignment in MyClass to work. I could make the IOption generic, but then the Base class would need to be generic, and then the WindowViewModel.ViewModels would also need to be redefined somehow. I dont want to make the Base generic as introducing generics there just makes everything else a real mess.

Question: is there a different way to declare MyFunc in IOption without using generics to allow assignment of Func<T,bool> in MyClass ?

public interface IOption
{
    public string Description {get; set;}
    public Expression<Func<object,bool>> MyFunc { get; set; }
}

public class Option : IOption
{
    public string Description {get; set;}
    public Expression<Func<object,bool>> MyFunc { get; set; }
}

public abstract class Base 
{
    public abstract ObservableCollection<Option> Options { get; set; }
    public abstract Option SelectedOption { get; set; }

    public abstract void RunIt();
}

public class Generic<T> : Base
{
    private DBContext _context;

    public override ObservableCollection<Option> Options { get; set; }
    public override Option SelectedOption { get; set; }

    public Generic()
       : base()
    {
        Options = new ObservableCollection<Option>();
    }
    public override void RunIt()
    {
        var result = _context.Set<T>().Where(SelectedOption?.MyFunc);
        // process result
    }
}

public class MyClass : Generic<MyType>
{
    public MyClass
       : base()
    {
        Func<MyType,bool> expression = t => t.MyDescription = "Hello World";
        Options.Add(new Option("Hi", expression));  // fail to compile type mismatch
        SelectedOption = Options.First();
    }
}

public class Special : Base
{
// do something else
}

public class WindowViewModel
{
   public WindowViewModel ()
   {
     MyViewModels = new ObservableCollection<Base>();
     MyViewModels.Add(new Special());
     MyViewModels.Add(new MyClass());
   }

   public ObservableCollection<Base> MyViewModels {get; set;}
   public Base SelectedViewModel { get; set; }

   public void DoRunIt()
   {
      SelectedViewModel.RunIt();
   }
}

one of the things I did try that compiles but throws runtime exception when used, is

Func<MyType,bool> expression = t => t.MyDescription = "Hello World";
MyFunc = t => expression((MyType)t);

There is a way to do this. It uses the ability for all delegates ( Func<MyType, bool> is a delegate) to be cast to Delegate .

You'd change IOption and Option to this:

public interface IOption
{
    public string Description { get; set; }
    Func<T, bool> GetMyFunc<T>();
}

public class Option : IOption
{
    string description;
    private Delegate expression;

    public Option(string description, Delegate expression)
    {
        this.description = description;
        this.expression = expression;
    }

    public string Description { get; set; }
    public Func<T, bool> GetMyFunc<T>() => (Func<T, bool>)this.expression;
}

Then MyClass works as expected (except for the other syntax error in your code).

You then just need to change RunIt on Generic<T> to this:

public override void RunIt()
{
    var result = _context.Set<T>().Where(SelectedOption?.GetMyFunc<T>());
    // process result
}

Question: is there a different way to declare MyFunc in IOption without using generics to allow assignment of Func<T,bool> in MyClass?

No, I don't believe that is possible. You can have generic methods in a non generic type, though.

However, there is an option that might work for you. You state

I dont want to make the Base generic as introducing generics there just makes everything else a real mess.

How about having both?

public abstract class Base<T>
{
    public abstract ObservableCollection<Option<T>> Options { get; set; }
    public abstract Option<T> SelectedOption { get; set; }

    public abstract void RunIt();
}

public abstract class Base : Base<object> { }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM