[组合约束]值和引用约束

rainychan2009 2013-12-04 08:52:35
加精
class SomeClass<T, U> where T: class where U: struct, T.
这样看来感觉有点矛盾啊,T有引用约束,U同时有值约束和引用约束,这样不就违背了任何一个类型不能同时是一个值类型和引用类型啊?
...全文
951 36 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
36 条回复
切换为时间正序
请发表友善的回复…
发表回复
rainychan2009 2013-12-09
  • 打赏
  • 举报
回复
引用 33 楼 u012892184 的回复:
这样看来感觉有点矛盾啊,T有引用约束,U同时有值约束和引用约束,这样不就违背了任何一个类型不能同时是一个值类型和引用类型啊?
具体到每一个变量的类型是固定的:要么是值类型的,要么是引用类型的,后面的约束是一种继承关系,值类型和引用类型都继承自Object,Interface也是一种引用,如果某种值类型实现了某种接口,那么他们之间就符合这种继承关系,只要找到所以不矛盾。比如 Int32 和ICompare<Int32>。其实:不仅仅类实例一种引用,接口变量也是引用,值类型变量实质上也是可以转换成引用。
_苏铁 2013-12-07
  • 打赏
  • 举报
回复
这样看来感觉有点矛盾啊,T有引用约束,U同时有值约束和引用约束,这样不就违背了任何一个类型不能同时是一个值类型和引用类型啊?
rainychan2009 2013-12-07
  • 打赏
  • 举报
回复
引用 29 楼 sj178220709 的回复:
从头梳理下: 1, int的定义 public struct Int32 : IComparable, IFormattable, IConvertible, IComparable<int>, IEquatable<int> 它虽然是struct 但本身也继承了这些接口 所以符合 where U : struct, T的约束 2,x.foo(1, 1); foo接受的参数类型是什么?不是int 而是IComparable SomeClass<IComparable<int>, int> x2 = new SomeClass<IComparable<int>, int>();同理 foo接受的参数类型是IComparable<int> 此过程等同与
  IComparable<int> i1 = 3;
                IComparable<int> i2 = 3;
                Console.WriteLine(i1 == i2);
输出false 可以说,这是一段莫名其妙的代码,因为IComparable的比较方式的调用方法不是== 而是CompareTo 比如 Console.WriteLine(3.CompareTo(3)); 以下是个人推测,未100%验证. 我们平常使用的== 是int类型重载了继承自object的==方法 所以3==3返回ture 而IComparable<int>的实例,没有去重载==,所以a==b,其实是调用的IComparable<int>的实例从object继承过来的方法,一个侧面可以证实此点的方法,诸位可以试试IComparable<string>,这次就会返回true了. 楼主这个问题也许没有实际意义,但确实让我加深了不少对c#底层的理解,非常感谢. 关于这个问题,可以引申出很多有意思的地方,我觉得我干脆写一篇博客算了.
值类型的装箱是独立的,跟你的值是否相同没有关系,这就是为什么看着都是3,为什么接口引用不相同,两次new,返回的地址怎么会是一样的呢? string本身就是一个引用类型的,没有装箱的过程,所以无论是引用本身,还是接口都是相同的。 这是针对这次讨论整理的一个笔记。http://blog.csdn.net/rainychan2009/article/details/17174033你可以看看。
  • 打赏
  • 举报
回复
引用 17 楼 sj178220709 的回复:
[quote=引用 15 楼 sj178220709 的回复:] [quote=引用 10 楼 caozhy 的回复:] 我用的是值类型。
namespace System
{
  /// <summary>
  /// 定义一种特定于类型的通用比较方法,值类型或类通过实现此方法对其实例进行排序。
  /// </summary>
  /// <filterpriority>1</filterpriority>
  [ComVisible(true)]
  public interface IComparable
  {
    /// <summary>
    /// 将当前实例与同一类型的另一个对象进行比较,并返回一个整数,该整数指示当前实例在排序顺序中的位置是位于另一个对象之前、之后还是与其位置相同。
    /// </summary>
    /// 
    /// <returns>
    /// 一个值,指示要比较的对象的相对顺序。 返回值的含义如下: 值 含义 小于零 此实例按排序顺序在 <paramref name="obj"/> 前面。 零 此实例与 <paramref name="obj"/> 在排序顺序中出现的位置相同。 大于零 此实例按排序顺序在 <paramref name="obj"/> 后面。
    /// </returns>
    /// <param name="obj">与此实例进行比较的对象。</param><exception cref="T:System.ArgumentException"><paramref name="obj"/> 不具有与此实例相同的类型。</exception><filterpriority>2</filterpriority>
    int CompareTo(object obj);
  }
确实是装箱了.[/quote] 咦 我使用了IComparable<int> 怎么还是false呢? 不过感觉应该还是IComparable的问题 待我再去看看底层实现.[/quote]
  /// <summary>
    /// 将此实例与指定对象进行比较并返回一个对二者的相对值的指示。
    /// </summary>
    /// 
    /// <returns>
    /// 一个有符号数字,指示此实例和 <paramref name="value"/> 的相对值。 返回值 说明 小于零 此实例小于 <paramref name="value"/>。 零 此实例等于 <paramref name="value"/>。 大于零 此实例大于 <paramref name="value"/>。 - 或 - <paramref name="value"/> 为 null。
    /// </returns>
    /// <param name="value">要比较的对象,或为 null。</param><exception cref="T:System.ArgumentException"><paramref name="value"/> 不是 <see cref="T:System.Int32"/>。</exception><filterpriority>2</filterpriority>
    public int CompareTo(object value);
    /// <summary>
    /// 将此实例与指定的 32 位有符号整数进行比较并返回对其相对值的指示。
    /// </summary>
    /// 
    /// <returns>
    /// 一个有符号数字,指示此实例和 <paramref name="value"/> 的相对值。 返回值 说明 小于零 此实例小于 <paramref name="value"/>。 零 此实例等于 <paramref name="value"/>。 大于零 此实例大于 <paramref name="value"/>。
    /// </returns>
    /// <param name="value">要比较的整数。</param><filterpriority>2</filterpriority>
    [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
    public int CompareTo(int value);
貌似还是装箱了啊,
  • 打赏
  • 举报
回复
引用 16 楼 caozhy 的回复:
你们没有理解我的意思 我是说,这样做没有意义。
好吧 这个到是真的,暂时没发现有什么实际情况是非要这么写的.
  • 打赏
  • 举报
回复
引用 15 楼 sj178220709 的回复:
[quote=引用 10 楼 caozhy 的回复:] 我用的是值类型。
namespace System
{
  /// <summary>
  /// 定义一种特定于类型的通用比较方法,值类型或类通过实现此方法对其实例进行排序。
  /// </summary>
  /// <filterpriority>1</filterpriority>
  [ComVisible(true)]
  public interface IComparable
  {
    /// <summary>
    /// 将当前实例与同一类型的另一个对象进行比较,并返回一个整数,该整数指示当前实例在排序顺序中的位置是位于另一个对象之前、之后还是与其位置相同。
    /// </summary>
    /// 
    /// <returns>
    /// 一个值,指示要比较的对象的相对顺序。 返回值的含义如下: 值 含义 小于零 此实例按排序顺序在 <paramref name="obj"/> 前面。 零 此实例与 <paramref name="obj"/> 在排序顺序中出现的位置相同。 大于零 此实例按排序顺序在 <paramref name="obj"/> 后面。
    /// </returns>
    /// <param name="obj">与此实例进行比较的对象。</param><exception cref="T:System.ArgumentException"><paramref name="obj"/> 不具有与此实例相同的类型。</exception><filterpriority>2</filterpriority>
    int CompareTo(object obj);
  }
确实是装箱了.[/quote] 咦 我使用了IComparable<int> 怎么还是false呢? 不过感觉应该还是IComparable的问题 待我再去看看底层实现.
threenewbee 2013-12-06
  • 打赏
  • 举报
回复
引用 15 楼 sj178220709 的回复:
[quote=引用 10 楼 caozhy 的回复:] 我用的是值类型。
namespace System
{
  /// <summary>
  /// 定义一种特定于类型的通用比较方法,值类型或类通过实现此方法对其实例进行排序。
  /// </summary>
  /// <filterpriority>1</filterpriority>
  [ComVisible(true)]
  public interface IComparable
  {
    /// <summary>
    /// 将当前实例与同一类型的另一个对象进行比较,并返回一个整数,该整数指示当前实例在排序顺序中的位置是位于另一个对象之前、之后还是与其位置相同。
    /// </summary>
    /// 
    /// <returns>
    /// 一个值,指示要比较的对象的相对顺序。 返回值的含义如下: 值 含义 小于零 此实例按排序顺序在 <paramref name="obj"/> 前面。 零 此实例与 <paramref name="obj"/> 在排序顺序中出现的位置相同。 大于零 此实例按排序顺序在 <paramref name="obj"/> 后面。
    /// </returns>
    /// <param name="obj">与此实例进行比较的对象。</param><exception cref="T:System.ArgumentException"><paramref name="obj"/> 不具有与此实例相同的类型。</exception><filterpriority>2</filterpriority>
    int CompareTo(object obj);
  }
确实是装箱了.[/quote] 你们没有理解我的意思 我是说,这样做没有意义。
  • 打赏
  • 举报
回复
引用 10 楼 caozhy 的回复:
我用的是值类型。
namespace System
{
  /// <summary>
  /// 定义一种特定于类型的通用比较方法,值类型或类通过实现此方法对其实例进行排序。
  /// </summary>
  /// <filterpriority>1</filterpriority>
  [ComVisible(true)]
  public interface IComparable
  {
    /// <summary>
    /// 将当前实例与同一类型的另一个对象进行比较,并返回一个整数,该整数指示当前实例在排序顺序中的位置是位于另一个对象之前、之后还是与其位置相同。
    /// </summary>
    /// 
    /// <returns>
    /// 一个值,指示要比较的对象的相对顺序。 返回值的含义如下: 值 含义 小于零 此实例按排序顺序在 <paramref name="obj"/> 前面。 零 此实例与 <paramref name="obj"/> 在排序顺序中出现的位置相同。 大于零 此实例按排序顺序在 <paramref name="obj"/> 后面。
    /// </returns>
    /// <param name="obj">与此实例进行比较的对象。</param><exception cref="T:System.ArgumentException"><paramref name="obj"/> 不具有与此实例相同的类型。</exception><filterpriority>2</filterpriority>
    int CompareTo(object obj);
  }
确实是装箱了.
threenewbee 2013-12-06
  • 打赏
  • 举报
回复
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication1
{
    class SomeClass<T, U>
        where T : class
        where U : struct, T
    {
        public void foo(T a, T b)
        {
            Console.WriteLine(a == b);
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            SomeClass<IFormattable, int> x = new SomeClass<IFormattable, int>();
            x.foo(1, 1);
        }
    }
}
不过你的理论有点问题 这样的代码还是可以编译的。
threenewbee 2013-12-06
  • 打赏
  • 举报
回复
引用 29 楼 sj178220709 的回复:
从头梳理下: 1, int的定义 public struct Int32 : IComparable, IFormattable, IConvertible, IComparable<int>, IEquatable<int> 它虽然是struct 但本身也继承了这些接口 所以符合 where U : struct, T的约束 2,x.foo(1, 1); foo接受的参数类型是什么?不是int 而是IComparable SomeClass<IComparable<int>, int> x2 = new SomeClass<IComparable<int>, int>();同理 foo接受的参数类型是IComparable<int> 此过程等同与
  IComparable<int> i1 = 3;
                IComparable<int> i2 = 3;
                Console.WriteLine(i1 == i2);
输出false 可以说,这是一段莫名其妙的代码,因为IComparable的比较方式的调用方法不是== 而是CompareTo 比如 Console.WriteLine(3.CompareTo(3)); 以下是个人推测,未100%验证. 我们平常使用的== 是int类型重载了继承自object的==方法 所以3==3返回ture 而IComparable<int>的实例,没有去重载==,所以a==b,其实是调用的IComparable<int>的实例从object继承过来的方法,一个侧面可以证实此点的方法,诸位可以试试IComparable<string>,这次就会返回true了. 楼主这个问题也许没有实际意义,但确实让我加深了不少对c#底层的理解,非常感谢. 关于这个问题,可以引申出很多有意思的地方,我觉得我干脆写一篇博客算了.
你可以写在论坛中,给你推荐。
  • 打赏
  • 举报
回复
从头梳理下: 1, int的定义 public struct Int32 : IComparable, IFormattable, IConvertible, IComparable<int>, IEquatable<int> 它虽然是struct 但本身也继承了这些接口 所以符合 where U : struct, T的约束 2,x.foo(1, 1); foo接受的参数类型是什么?不是int 而是IComparable SomeClass<IComparable<int>, int> x2 = new SomeClass<IComparable<int>, int>();同理 foo接受的参数类型是IComparable<int> 此过程等同与
  IComparable<int> i1 = 3;
                IComparable<int> i2 = 3;
                Console.WriteLine(i1 == i2);
输出false 可以说,这是一段莫名其妙的代码,因为IComparable的比较方式的调用方法不是== 而是CompareTo 比如 Console.WriteLine(3.CompareTo(3)); 以下是个人推测,未100%验证. 我们平常使用的== 是int类型重载了继承自object的==方法 所以3==3返回ture 而IComparable<int>的实例,没有去重载==,所以a==b,其实是调用的IComparable<int>的实例从object继承过来的方法,一个侧面可以证实此点的方法,诸位可以试试IComparable<string>,这次就会返回true了. 楼主这个问题也许没有实际意义,但确实让我加深了不少对c#底层的理解,非常感谢. 关于这个问题,可以引申出很多有意思的地方,我觉得我干脆写一篇博客算了.
  • 打赏
  • 举报
回复
我终于想明白了. 原来box不是发生在底层 而是在 x.foo(1, 1);这里就已经装箱了 里面a,b的类型不是int 而是System.IComparable {int}
davidhxy1234 2013-12-06
  • 打赏
  • 举报
回复
抱歉,没看清代码,原来还是没办法构造出这样的类型,不过还是学到一些东西
davidhxy1234 2013-12-06
  • 打赏
  • 举报
回复
从你的帖子可以看出 没有任何类型是引用类型又是值类型 这句话是错误的,这就是探究这个问题的意义所在。
rainychan2009 2013-12-06
  • 打赏
  • 举报
回复
引用 16 楼 caozhy 的回复:
[quote=引用 15 楼 sj178220709 的回复:] [quote=引用 10 楼 caozhy 的回复:] 我用的是值类型。
namespace System
{
  /// <summary>
  /// 定义一种特定于类型的通用比较方法,值类型或类通过实现此方法对其实例进行排序。
  /// </summary>
  /// <filterpriority>1</filterpriority>
  [ComVisible(true)]
  public interface IComparable
  {
    /// <summary>
    /// 将当前实例与同一类型的另一个对象进行比较,并返回一个整数,该整数指示当前实例在排序顺序中的位置是位于另一个对象之前、之后还是与其位置相同。
    /// </summary>
    /// 
    /// <returns>
    /// 一个值,指示要比较的对象的相对顺序。 返回值的含义如下: 值 含义 小于零 此实例按排序顺序在 <paramref name="obj"/> 前面。 零 此实例与 <paramref name="obj"/> 在排序顺序中出现的位置相同。 大于零 此实例按排序顺序在 <paramref name="obj"/> 后面。
    /// </returns>
    /// <param name="obj">与此实例进行比较的对象。</param><exception cref="T:System.ArgumentException"><paramref name="obj"/> 不具有与此实例相同的类型。</exception><filterpriority>2</filterpriority>
    int CompareTo(object obj);
  }
确实是装箱了.[/quote] 你们没有理解我的意思 我是说,这样做没有意义。[/quote] 这里的没有意义应该是:本来传入一个值类型,最终输出的结果却跟我们的预期是偏差的,但是这种偏差本身是可以解释的:装箱了之后,引用肯定不一样,说到底,只能传入引用进行比较才有意义,不过引用的比较确实没有必要搞这么一套。 但是在语言层面,我觉得还是有一点意义的。特别是对于像我这种刚接触C#的人来说:1)约束 引用和值约束,讨论的只是一个可否为null。对于引用,比较的就是引用,一个地址,类型相同可以比较,但是值类型呢?基于泛型,就没法比较,极端的例子,两个浮点数进行==元算就是不合理的,编译器直接不买账(这样理解是否可行?) 2 装箱是个从有到有的过程,其实就是开辟空间进行转存,但是拆箱是一个从有到可能有的过程,需要提供正确的类型,否则拆出来的东西也没法用,所以,如果约束为引用的话,值类型的传入是没有问题的,反正比较的是引用,这就好比,本来想比较下两同学的成绩的,但是编译器只看学号的大小了,学号可以理解为对同学的引用。 3 值类型本身也实现了接口,另外值类型都有一个默认的无参构造函数,这个就是可以实现 where T:class where U: struct, T这种约束的原因。
threenewbee 2013-12-05
  • 打赏
  • 举报
回复
我用的是值类型。
rainychan2009 2013-12-05
  • 打赏
  • 举报
回复
引用 6 楼 caozhy 的回复:
我们看为什么用class引用: http://msdn.microsoft.com/zh-cn/library/d5x73970.aspx 这是MSDN的描述 在应用 where T : class 约束时,避免对类型参数使用 == 和 != 运算符,因为这些运算符仅测试引用同一性而不测试值相等性。 即使在用作参数的类型中重载这些运算符也是如此。 下面的代码说明了这一点;即使 String 类重载 == 运算符,输出也为 false。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication1
{
    class SomeClass<T, U>
        where T : class
        where U : struct, T
    {
        public void foo(T a, T b)
        {
            Console.WriteLine(a == b);
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            SomeClass<IComparable, int> x = new SomeClass<IComparable, int>();
            x.foo(1, 1);
        }
    }
}
False Press any key to continue . . . 如果去掉 where T : class Console.WriteLine(a == b); 不能通过编译。 但是,很显然,对于值类型,这样的使用返回的是false,显然是错误的。 所以我还是看不出这样做有什么实际意义。
"很显然,对于值类型,这样的使用返回的是false,显然是错误的" 这个说法不对啊,你用的是引用类型,只是有一个隐藏的装箱操作,这样没有任何不合理的地方。
rainychan2009 2013-12-05
  • 打赏
  • 举报
回复
引用 3 楼 caozhy 的回复:
这样写没有任何问题,但是你没法用,因为你构造不出一个U类型继承自一个引用类型同时又是结构体。

我昨晚发Email给作者了,原来这样的用法完全是ok的。

话说能申精吗,能够让更多的人理解这一点

threenewbee 2013-12-05
  • 打赏
  • 举报
回复
我们看为什么用class引用:
http://msdn.microsoft.com/zh-cn/library/d5x73970.aspx

这是MSDN的描述

在应用 where T : class 约束时,避免对类型参数使用 == 和 != 运算符,因为这些运算符仅测试引用同一性而不测试值相等性。 即使在用作参数的类型中重载这些运算符也是如此。 下面的代码说明了这一点;即使 String 类重载 == 运算符,输出也为 false。


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication1
{
class SomeClass<T, U>
where T : class
where U : struct, T
{
public void foo(T a, T b)
{
Console.WriteLine(a == b);
}
}

class Program
{
static void Main(string[] args)
{
SomeClass<IComparable, int> x = new SomeClass<IComparable, int>();
x.foo(1, 1);
}
}
}


False
Press any key to continue . . .

如果去掉
where T : class

Console.WriteLine(a == b); 不能通过编译。

但是,很显然,对于值类型,这样的使用返回的是false,显然是错误的。

所以我还是看不出这样做有什么实际意义。
rainychan2009 2013-12-04
  • 打赏
  • 举报
回复
大佬们都这样说,看来我只能继续朝下看了
加载更多回复(3)

111,093

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • AIGC Browser
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧