locked
Compare and Assign a Func/Delegete Results

    Question

  • I have a class called 'Something'.  I would like to save the property values of an instance of 'Something' to another 'Something' based on a default 'Something'.  Each property needs to go through the exact same steps to determine how to save the instance of 'Something'.  That's why I'm trying to put that algorithm into a single method and pass each property to it. 

    I created a SaveValue method where I provide the 'Something' classes and a Func object that is passed s => s.property.  The GetValue method turned out fine since it only ever compared the Func object to null and then returned the Func object.  The SaveValue method is trying to either compare the Func object to another Func object or assign it a value.  Trying to compare or assign the Func object a value seems to cause the compiler no end of grief.  Since the Func object is nothing more that Something.property is there a way to tel the compiler to use the results of the Func object instead of the Func object itself?

    Here is a link to the project. The project has a working GetValue method but the SaveValue is where I'm having a problem.  I've also included each of the project sections below with a brief description of what it does.

    Ryan

    A basic with two properties that need to be saved.

    public class Something
    {
    	public Something() {}
    	public Something(string here) { Here = here; }
    	public Something(int there ) { There = there; }
    	public Something(string here, int there) { Here = here; There = there; }
    		
    	public string Here  { get; set; }
    	public int    There { get; set; }
    }
    

    The main shows a working method to get values from two different 'Something' variables.  The main also shows how I'm trying to save each 'Something' property.

    public class Program
    {
    	public static void Main(string[] args)
    	{
    		var mySomething = new Something("Hello");
    		var mySomething2 = new Something(15);
    		var mySomething3 = new Something("World", 3);
    
    		Console.WriteLine(mySomething.GetValue(mySomething2, s => s.Here).ToString());
    		Console.WriteLine(mySomething.GetValue(mySomething2, s => s.There).ToString());
    
    		mySomething3.SaveValue(mySomething, mySomething2, s => s.Here);
    		mySomething3.SaveValue(mySomething, mySomething2, s => s.There);
    	}
    }
    

    Here is the utility class that is providing both the load/save methods for the 'Something' class.

    public static class Util
    {
    	public static TProperty GetValue<TProperty>(this Something first, Something second, Func<Something, TProperty> item)
    	{
    		if (first != null)
    			if (item(first) != null)
    				return item(first);
    			
    		if (second != null)
    			if (item(second) != null)
    				return item(second);
    			else
    				throw new ArgumentException("The item is missing from the 'second' location");
    				
    		throw new ArgumentException("The item is missing in the 'first' and 'second' location");
    	}
    		
    	public static void SaveValue<TProperty>(this Something first, ref Something second, Something third, Func<Something, TProperty> item)
    	{
    		if (third != null)
    		{
    			if (item(first) != item(third))
    			{
    				if (second == null)
    					second = new Something();
    					
    				item(second) = item(first);
    			}
    			else
    				item(second) = null;
    				
    		}
    		else
    		{
    			if (second == null)
    				second = new Something();
    				
    			item(second) = item(first);
    		}
    	}
    }

    Tuesday, April 4, 2017 6:03 PM

Answers

  • Hi Ryan Software,

    Thank you for posting here.

    For your question, you could try the following code.

     public static void SaveValue<TProperty>(this Something first, ref Something second, Something third, Func<Something, TProperty> item)
            {
                TProperty s = item(second);
                if (third != null)
                {
                    if (!item(first).Equals(item(third)))
                    {
                        if (second == null)
                            second = new Something();
    
                        s = item(first);
                    }
                    else
                        s = default(TProperty);
    
                }
                else
                {
                    if (second == null)
                        second = new Something();
    
                    s = item(first);
                }
            }

    First, the left-hand side of assignment must be a variable, property or indexer.

    Second, could not convert null to type parameter "TProperty". For the nullable value types, you could use default to return System.Nullable<T>.

    I hope this would be helpful.

    Best Regards,

    Wendy


    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    Thursday, April 6, 2017 3:29 AM
    Moderator

All replies

  • Hi Ryan Software,

    Thank you for posting here.

    For your question, you could try the following code.

     public static void SaveValue<TProperty>(this Something first, ref Something second, Something third, Func<Something, TProperty> item)
            {
                TProperty s = item(second);
                if (third != null)
                {
                    if (!item(first).Equals(item(third)))
                    {
                        if (second == null)
                            second = new Something();
    
                        s = item(first);
                    }
                    else
                        s = default(TProperty);
    
                }
                else
                {
                    if (second == null)
                        second = new Something();
    
                    s = item(first);
                }
            }

    First, the left-hand side of assignment must be a variable, property or indexer.

    Second, could not convert null to type parameter "TProperty". For the nullable value types, you could use default to return System.Nullable<T>.

    I hope this would be helpful.

    Best Regards,

    Wendy


    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    Thursday, April 6, 2017 3:29 AM
    Moderator
  • "it is not possible to call neither the method GetValue() nor SaveValue() on an instance of type Something! The reason is the absence of such methods in Something type definition."

    Other than the fact that the SaveValue body isn't compiling, hence the post, the code will compile. These methods are defined as extension methods in the Util class which was posted last in the original post.  Methods do not need to be defined on a type in question to be called as instance methods. That is what extension methods are for.

    Saturday, April 8, 2017 3:12 PM
    Moderator
  • The OP asked for assistance getting their code to compile. Wendy has done that. Criticizing their code helps no one. Especially when criticism tends to be subjective.

    Issue 1 - This is incorrect. You do not use a ref parameter to return a value. Ref indicates you are modifying a value already passed in. To return additional values outside the return type of a method you would use out parameters. However there are cases where returning a value is inefficient compared to using ref to modify an existing value. Complex objects that are expensive to create and/or copy are great examples of this. So having a unconditional rule that says ref is bad is unwise to me.

    Issue 2 - Not checking for null on the source argument is the correct behavior for extension methods. Extension methods are expected to behave like instance methods. If you use an instance method on null you'll get a NullReferenceException. This is a system exception. All guidelines from MS and other companies are clear that you should never throw a system exception. Instead you would traditionally throw an ArgumentNullException. But doing that causes different exceptions to be thrown and is generally considered inconsistent. Instead you should throw NullRefenceException just like an instance method. But given the guideline that you shouldn't throw system exceptions the best way to get this same behavior is to simply not check for null and let the method throw naturally when/if the parameter is used.

    Issue 3 - I don't understand your logic here at all. The method can be used with any instance of Something. The only thing that was initially being done inside the method is that the second parameter is being initialized if it is null. There is nothing wrong with that. I fail to see how default instances make any difference here.

    Irrelevant, the OP asked for help getting their code to compile. Wendy posted a valid solution. If you would like to post a better implementation then feel free to do so and the OP can decide which approach they want to go with. Simply criticizing their code isn't helping them.

    Sunday, April 9, 2017 5:51 AM
    Moderator
  • Once again you feel the need to claim your rightness over everyone else and then tell them they need to learn the language. Once again you have taken a reasonable discussion and turned it abusive when people don't agree with you.  Therefore I will once again mark your offensive posts accordingly which will notify the administrators that you are violating the rules of the forums. If you continue down this path of insulting everyone in the forums you will get banned. This isn't really negotiable. Everyone in the forums are expected to follow the same rules.

    I'm also locking this thread so that you cannot continue to throw out offensive posts since clearly this conversation isn't going to help the OP.

    Sunday, April 9, 2017 2:15 PM
    Moderator