3

This is an enhancement on my previous question on specification pattern - How to combine conditions dynamically?.

I am trying to make the OnSaleSpecificationForBook method a generic one. The reason being the AudioCD logic also needs a similar specification and both Book and AudioCD implements ISellingItem interface.

Specification

public class OnSaleSpecificationForBook : Specification<Book>
{
    public override bool IsSatisfiedBy(Book product)
    {
        return product.IsOnSale;
    }
}

I tried to create a generic method as listed below but it throws following error:

The type or namespace name 'T' could not be found

Code with compilation error

public class OnSaleSpecification : Specification<T>
{
    public override bool IsSatisfiedBy(T item)
    {
        return item.IsOnSale;
    }
}

QUESTIONS

  1. What is the reason for this error?
  2. How can we make this method generic?

Note: I am using .Net 4.0. However I would like to know if there is any difference needed when compared with .Net 2.0

Abstractions

public interface ISellingItem
{
    bool IsOnSale { get; set; }
    double Price { get; set; }
}

public abstract class Specification<T>
{
    public abstract bool IsSatisfiedBy(T obj);
}

Client

class Program
{       
    static void Main(string[] args)
    {
        List<Book> list = new List<Book>();

        Book p1 = new Book(false, 99);
        Book p2 = new Book(true, 99);
        Book p3 = new Book(true, 101);

        list.Add(p1);
        list.Add(p2);
        list.Add(p3);

        var specification = new OnSaleSpecificationForBook();
        List<Book> selectedList =
            ProductFilterHelper.GetProductsUisngDynamicFilters(list, specification);
    }
}

public static class ProductFilterHelper
{
    public static List<Book> GetProductsUisngDynamicFilters(List<Book> productList, Specification<Book> productSpecification)
    {
        return productList.Where(p => productSpecification.IsSatisfiedBy(p))
                          .ToList();
    }
}

Entities

public class Book : ISellingItem
{
    public bool IsOnSale { get; set; }
    public double Price { get; set; }

    public Book(bool isOnSale, double price)
    {
        this.Price = price;
        this.IsOnSale = isOnSale;
    }
}

public class AudioCD : ISellingItem
{
    public bool IsOnSale { get; set; }
    public double Price { get; set; }

    public AudioCD(bool isOnSale, double price)
    {
        this.Price = price;
        this.IsOnSale = isOnSale;
    }
}
Community
  • 1
  • 1
LCJ
  • 22,196
  • 67
  • 260
  • 418
  • 1
    Reference: [Why use generic constraints in C#](http://stackoverflow.com/questions/4073852/why-use-generic-constraints-in-c-sharp) – LCJ Jan 29 '14 at 17:30

2 Answers2

3

You need to specify what the generic parameter's type is implementing before the compiler will know that it is an ISellingItem. You can do this with a where T: ISellingItem clause:

public class OnSaleSpecification<T> : Specification<T> where T : ISellingItem
{
    public override bool IsSatisfiedBy(T item)
    {
        return item.IsOnSale;
    }
}
CassOnMars
  • 6,153
  • 2
  • 32
  • 47
  • 1
    This won't compile unless you also make `OnSaleSpecification` generic, as well. – Ian McLaird Jan 29 '14 at 17:14
  • Lijo: I've edited the answer. @Ian McLaird: Good catch. Thanks. – CassOnMars Jan 29 '14 at 17:16
  • @IanMcLaird What is the advantage that the compiler is getting when we make the `class` generic? In other words, why the compiler is forcing the class to be generic? What would happen otherwise? – LCJ Jan 29 '14 at 17:18
  • 1
    You can't extend an unbound generic type. All of the type parameters (`` in this case) have to be defined before the `:` or be an explicit known type. – Ian McLaird Jan 29 '14 at 17:25
  • 2
    Also note that in this *specific* case, you could make it `OnSaleSpecification : Specification`, with no loss of functionality for *this* method. However, if you did that, you'd lose the ability to add additional methods that might actually care about the real type. – Ian McLaird Jan 29 '14 at 17:29
  • @IanMcLaird I am not clear about the statement - `you'd lose the ability to add additional methods that might actually care about the real type.`. Can you please elaborate it with some code? – LCJ Jan 30 '14 at 07:29
  • @Lijo, the accepted answer to the question you linked to in the comments on this question demonstrates that point with code. – Ian McLaird Jan 30 '14 at 14:15
0

Your class OnSaleSpecification need to define the generic parameter T and constrain it to an ISellingItem

public class OnSaleSpecification<T> : Specification<T> where T : ISellingItem
{
    public override bool IsSatisfiedBy(T item)
    {
        return item.IsOnSale;
    }
}
James Hay
  • 12,580
  • 8
  • 44
  • 67
  • This won't work without a constraint on `T` since it has no `IsOnSale` property. – Lee Jan 29 '14 at 17:12