Results 1 to 5 of 5

Thread: Need help with generics and base classes

  1. #1

    Thread Starter
    Frenzied Member
    Join Date
    May 2002
    Posts
    1,602

    Need help with generics and base classes

    Hi!

    I am trying to apply some refactoring to a viewmodel class, but I am fairly stuck. We had validation logic in every view model, and the code looked very repetitive, so I figured I should put int in our ViewModelBase class. But it just won't work correctly.

    I have two problems:

    1) Should I really need a generic base class where T is the same type as the inheriting class? Feels like it is the same thing...
    2) In the base class I need to use an extension method that only seems to be available in a derived class. We are using FluentValidator library.

    Here is the code:

    Code:
    Public Class MainViewModel
    	Inherits ViewModelBase(Of MainViewModel)
    	Implements IDataErrorInfo
    	Private _price As System.Nullable(Of Decimal)
    	Public Property Price() As System.Nullable(Of Decimal)
    		Get
    			Return _price
    		End Get
    		Set
    			SetProperty(_price, value)
    			Debug.WriteLine("Price set to:{0}", value)
    		End Set
    	End Property
    
    
    	Public _name As String
    	Public Property Name() As String
    		Get
    			Return _name
    		End Get
    		Set
    			SetProperty(_name, value)
    		End Set
    	End Property
    
    	Public Sub New(validator As AbstractValidator(Of MainViewModel))
    
    
    		MyBase.New(validator)
    	End Sub
    End Class
    
    
    Public MustInherit Class ViewModelBase(Of Tv)
    	Implements INotifyPropertyChanged
    	Public Event PropertyChanged As PropertyChangedEventHandler
    	Public Property Validator() As AbstractValidator(Of Tv)
    		Get
    			Return m_Validator
    		End Get
    		Set
    			m_Validator = Value
    		End Set
    	End Property
    	Private m_Validator As AbstractValidator(Of Tv)
    	Private _errors As Dictionary(Of String, String)
    
    	Public Sub New(validator__1 As AbstractValidator(Of Tv))
    		Validator = validator__1
    		_errors = New Dictionary(Of String, String)()
    	End Sub
    	Protected Sub SetProperty(Of T)(ByRef field As T, value As T, <CallerMemberName> Optional name As String = "")
    		If Not EqualityComparer(Of T).[Default].Equals(field, value) Then
    			field = value
    			Dim handler = PropertyChanged
    			RaiseEvent handler(Me, New PropertyChangedEventArgs(name))
    		End If
    	End Sub
    
    	Public Default ReadOnly Property Item(propertyName As String) As String
    		Get
    			Dim errors As String = String.Empty
    			If Validator IsNot Nothing Then
    				_errors.Remove(propertyName)
    
    				Dim results = Validator.Validate(Me, propertyName)
    				If results IsNot Nothing AndAlso results.Errors.Count > 0 Then
    					errors = String.Join(Environment.NewLine, results.Errors.[Select](Function(x) x.ErrorMessage).ToArray())
    					_errors.Add(propertyName, errors)
    				End If
    			End If
    			[Error] = [String].Join(Environment.NewLine, _errors.[Select](Function(x) x.Value).ToArray())
    			Return errors
    		End Get
    	End Property
    
    
    	Private _error As String
    	Public Property [Error]() As String
    		Get
    			Return _error
    		End Get
    		Set
    			SetProperty(_error, value)
    		End Set
    	End Property
    End Class

    There is an implementation of MainViewModelValidator where the validation logic is contained. That should be injected to MainViewModel through constructor and passed down to the base class. The real issue I think is the declaration of the property that holds the Validator. From what I understand, this has to be a generic with a specific type, hence I need to pass down the type T of the MainViewModel, so I can declare a property that holds my validator.

    The next problem is the Validator.Validate line which won't compile because this particular signature of Validate with 2 parameters is an extensionmethod and not available in the AbstractValidator(of T) base class. Should I do some kind of cast here or what?


    It feels like I am on the right track here, but this is my first attempt with generics and I feel kind of lost.

    please help me out, it would save us tons of code since we have like 20 viewmodels with the same validator code...

    cheers
    Henrik

  2. #2

    Thread Starter
    Frenzied Member
    Join Date
    May 2002
    Posts
    1,602

    Re: Need help with generics and base classes

    Hi again!

    As I noted before, the compiler doesn't like the call to validate, which is an extension method definied in FluentValidation namespace.

    As far as I can see, the real issue here is that I am trying to call this extension method from a class that is generic. If I put all this code directly in the MainViewModel without the generics, it works just fine. So I guess my question boils down to this:

    How can I call Validate from a generic base class? The extension method itself is declared ina static non generic class within the FluentValidation namespace. Do I have to write some kind of wrapper or is there some other coding magic I can use?

    I am quite clueless on this one to be honest. The strange thing is alos that intellisense suggests there is a method with 2 parameters, but refuse to accept my call to it. Maybe a bug?

    /H

  3. #3
    Super Moderator Shaggy Hiker's Avatar
    Join Date
    Aug 2002
    Location
    Idaho
    Posts
    40,109

    Re: Need help with generics and base classes

    I think the generics is (are) a mistake. This looks like a simple parent-child relationship where the base class must be overriden by a child class. For example, the validator seems like a place where polymorphism applies. It looks like your AbstractValidator method should be nothing more than a private member of the derived class. If the base class has a MustOverride AbstractValidator function then the derived class will HAVE to have such a method. The Validator method found in the base class would then call whichever AbstractValidator the child class implemented, depending on what the real type is. This assumes that you'd create a class of type MainViewModel and put it in a variable of type ViewModelBase.
    My usual boring signature: Nothing

  4. #4

    Thread Starter
    Frenzied Member
    Join Date
    May 2002
    Posts
    1,602

    Re: Need help with generics and base classes

    Hi!

    I am not sure I understand.

    AbstractValidator(of T) is a class defined in the nuget package FluentValidation and is nothing I can change. The problem I am facing is that the Validate extension method only seem to work in the exact same class as it is defined in.

    For example, if I declare

    dim validator as AbstractValidator(of ClassA)

    I can only call

    validator.Validate(me, "property")

    in ClassA.

    If I try to do the same from ClassB, the extension method will fail.


    If I in my example declare the validator as

    public property Validator as AbstractValidator(of ViewModelBase(of Tv))

    everything is happy at this end, but I will get problems when I try to assign my AbstractValidator(of MainViewModel) to this property.

    I hope I have made this a little more clear.

    My main objective is to have the validation logic from FluentAssertions in a base viewmodel, and not the exact same code (from IErrorDataInfo) in every viewmodel.

    cheers
    H

  5. #5
    PowerPoster Evil_Giraffe's Avatar
    Join Date
    Aug 2002
    Location
    Suffolk, UK
    Posts
    2,555

    Re: Need help with generics and base classes

    From reading the code, I would say your problem is that when you call Validator.Validate() in ViewModelBase(Of Tv), you are passing it Me as a parameter. Me is of type ViewModelBase(Of Tv), but Validator is an AbstractValidator(Of Tv), so the fist parameter of the Validate method is almost certainly requiring a Tv, and ViewModelBase(Of Tv) is not convertible to that.

    Frankly, I'd say inheritance and generics are a poor fit for this problem, but if you want to continue with this approach, I would stop calling Validator.Validate from ViewModelBase(Of Tv), instead call a MustOverride method for validating that the derived classes can implement by simply calling Validator.Validate(Me, whatever), because in MyDerivedClass Validator is typed as AbstractValidator(Of MyDerivedClass) and therefore Me works just fine as an argument as far as the compiler is concerned. I think this is almost what SH suggested.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width